mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-01 14:56:30 +00:00
replaced custom auth with existing methods
This commit is contained in:
parent
245a54532f
commit
597a948e33
@ -14,16 +14,12 @@ menu:
|
|||||||
|
|
||||||
# Arch package registry
|
# Arch package registry
|
||||||
|
|
||||||
Gitea has arch package registry, which can act as a fully working [arch linux mirror](https://wiki.archlinux.org/title/mirrors) and connected directly in `/etc/pacman.conf`. Gitea automatically creates pacman database for packages in user space when new arch package is uploaded.
|
Gitea has arch package registry, which can act as a fully working [arch linux mirror](https://wiki.archlinux.org/title/mirrors) and connected directly in `/etc/pacman.conf`. Gitea automatically creates pacman database for packages in user/organization space when new arch package is uploaded.
|
||||||
|
|
||||||
**Table of Contents**
|
**Table of Contents**
|
||||||
|
|
||||||
{{< toc >}}
|
{{< toc >}}
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
You can install packages in any environment with [pacman](https://wiki.archlinux.org/title/Pacman). Alternatively you can use [pack](https://fmnx.su/core/pack) which connects specified registries automatically and provides simple interface for package uploads and deletions.
|
|
||||||
|
|
||||||
## Install packages
|
## Install packages
|
||||||
|
|
||||||
First, you need to update your pacman configuration, adding following lines:
|
First, you need to update your pacman configuration, adding following lines:
|
||||||
@ -39,136 +35,37 @@ Then, you can run pacman sync command (with -y flag to load connected database f
|
|||||||
pacman -Sy package
|
pacman -Sy package
|
||||||
```
|
```
|
||||||
|
|
||||||
## GPG Verification
|
|
||||||
|
|
||||||
Upload and remove operation are validated with [GnuPG](https://gnupg.org/). First, you need to export and upload your public gpg key to `SSH/GPG Keys` in account settings. This works similarly to SSH keys. You can export gpg key with command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
gpg --armor --export
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
|
|
||||||
mQENBGSYoJUBCADSJ6v8Egst/gNJVC2206o8JqTzRBxTULKm/DH5J7AzrhJBxC2/
|
|
||||||
...
|
|
||||||
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
```
|
|
||||||
|
|
||||||
## Upload packages
|
## Upload packages
|
||||||
|
|
||||||
1. Ensure, that your package have been signed with your gpg key (more about arch [package signing](https://wiki.archlinux.org/title/DeveloperWiki:Package_signing)). You can do that by running following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
gpg --verify package-ver-1-x86_64.pkg.tar.zst.sig
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Sign message metadata, which consists of package owner (namespace in gitea), package file name and send time. You can do that by running following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
echo -n {owner}{package}$(date --rfc-3339=seconds | tr " " T) >> md
|
|
||||||
gpg --detach-sign md
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Decode message and metadata signatures to hex, by running following commands, save output somewhere.
|
1. Decode message and metadata signatures to hex, by running following commands, save output somewhere.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
xxd -p md.sig >> md.sig.hex
|
xxd -p package-1-1-x86_64.pkg.tar.zst.sig >> package-signature-hex
|
||||||
xxd -p package-1-1-x86_64.pkg.tar.zst.sig >> pkg.sig.hex
|
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Paste your parameters and push package with [curl](https://curl.se/). Important, that time should be the same with metadata (signed md file), since this value is verified with GnuPG.
|
2. Paste your parameters and push package with [curl](https://curl.se/). Important, that time should be the same with metadata (signed md file), since this value is verified with GnuPG.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -X PUT \
|
curl -X PUT \
|
||||||
'https://{domain}/api/packages/{owner}/arch/push' \
|
'https://{domain}/api/packages/{owner}/arch/push' \
|
||||||
--header 'filename: {package}-1-1-x86_64.pkg.tar.zst' \
|
--header 'filename: package-1-1-x86_64.pkg.tar.zst' \
|
||||||
--header 'email: dancheg97@fmnx.su' \
|
|
||||||
--header 'distro: archlinux' \
|
--header 'distro: archlinux' \
|
||||||
--header 'time: {metadata-time}' \
|
|
||||||
--header 'pkgsign: {package-signature-hex}' \
|
--header 'pkgsign: {package-signature-hex}' \
|
||||||
--header 'metasign: {metadata-signature-hex}' \
|
|
||||||
--header 'Content-Type: application/octet-stream' \
|
--header 'Content-Type: application/octet-stream' \
|
||||||
--data-binary '@/path/to/package/file/{package}-1-1-x86_64.pkg.tar.zst'
|
--data-binary '@/path/to/package/file/package-1-1-x86_64.pkg.tar.zst'
|
||||||
```
|
|
||||||
|
|
||||||
Full script for package upload:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
owner=user
|
|
||||||
package=package-0.1.0-1-x86_64.pkg.tar.zst
|
|
||||||
email=user@example.com
|
|
||||||
|
|
||||||
time=`date --rfc-3339=seconds | tr " " T`
|
|
||||||
pkgsignhex=`xxd -p $package.sig | tr -d "\n"`
|
|
||||||
|
|
||||||
echo -n $owner$package$time >> mddata
|
|
||||||
gpg --detach-sign mddata
|
|
||||||
mdsignhex=`xxd -p mddata.sig | tr -d "\n"`
|
|
||||||
|
|
||||||
curl -X PUT \
|
|
||||||
http://{domain}/api/packages/$owner/arch/push \
|
|
||||||
--header "filename: $package" \
|
|
||||||
--header "email: $email" \
|
|
||||||
--header "time: $time" \
|
|
||||||
--header "distro: archlinux" \
|
|
||||||
--header "metasign: $mdsignhex" \
|
|
||||||
--header "pkgsign: $pkgsignhex" \
|
|
||||||
--header 'Content-Type: application/octet-stream' \
|
|
||||||
--data-binary @$package
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Delete packages
|
## Delete packages
|
||||||
|
|
||||||
1. Prepare signature for delete message.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
echo -n {owner}{package}$(date --rfc-3339=seconds | tr " " T) >> md
|
|
||||||
gpg --detach-sign md
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Send delete message with [curl](https://curl.se/). Time should be the same with saved in `md` file.
|
1. Send delete message with [curl](https://curl.se/). Time should be the same with saved in `md` file.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -X DELETE \
|
curl -X DELETE \
|
||||||
http://localhost:3000/api/packages/{user}/arch/remove \
|
http://localhost:3000/api/packages/{user}/arch/remove \
|
||||||
--header "username: {user}" \
|
|
||||||
--header "email: user@email.com" \
|
|
||||||
--header "target: package" \
|
--header "target: package" \
|
||||||
--header "time: {rmtime}" \
|
--header "version: {version-release}"
|
||||||
--header "version: {version-release}" \
|
|
||||||
--header 'Content-Type: application/octet-stream' \
|
|
||||||
--data-binary @md.sig
|
|
||||||
```
|
|
||||||
|
|
||||||
Full script for package deletion:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
owner=user
|
|
||||||
package=package
|
|
||||||
version=0.1.0-1
|
|
||||||
email=user@example.com
|
|
||||||
arch=x86_64
|
|
||||||
time=`date --rfc-3339=seconds | tr " " T`
|
|
||||||
|
|
||||||
sudo rm -rf md md.sig
|
|
||||||
echo -n $owner$package$time >> md
|
|
||||||
gpg --detach-sign md
|
|
||||||
|
|
||||||
curl -X DELETE \
|
|
||||||
http://{domain}/api/packages/$owner/arch/remove \
|
|
||||||
--header "username: $owner" \
|
|
||||||
--header "email: $email" \
|
|
||||||
--header "target: $package" \
|
|
||||||
--header "time: $time" \
|
|
||||||
--header "version: $version" \
|
|
||||||
--header 'Content-Type: application/octet-stream' \
|
|
||||||
--data-binary @md.sig
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Clients
|
## Clients
|
||||||
|
|
||||||
You can generate client code with tools like [thunder client](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client), [postman](https://blog.postman.com/curl-and-postman-work-wonderfully-together/) or other code generators to write your own client.
|
You can use gitea CLI tool to - [tea](https://gitea.com/gitea/tea) to push/remove arch packages from gitea. Alternatively, you can try [pack](https://fmnx.su/core/pack).
|
||||||
|
|
||||||
Also you can take a look at [pack](https://fmnx.su/core/pack) which provides `pacman` functionality with additional commands to build, sign and push your arch packages to gitea.
|
|
||||||
|
@ -124,8 +124,8 @@ func CommonRoutes() *web.Route {
|
|||||||
})
|
})
|
||||||
}, reqPackageAccess(perm.AccessModeRead))
|
}, reqPackageAccess(perm.AccessModeRead))
|
||||||
r.Group("/arch", func() {
|
r.Group("/arch", func() {
|
||||||
r.Put("/push", arch.Push)
|
r.Put("/push", arch.Push, reqPackageAccess(perm.AccessModeWrite))
|
||||||
r.Delete("/remove", arch.Remove)
|
r.Delete("/remove", arch.Remove, reqPackageAccess(perm.AccessModeWrite))
|
||||||
r.Get("/{distro}/{arch}/{file}", arch.Get)
|
r.Get("/{distro}/{arch}/{file}", arch.Get)
|
||||||
})
|
})
|
||||||
r.Group("/cargo", func() {
|
r.Group("/cargo", func() {
|
||||||
|
@ -23,20 +23,10 @@ func Push(ctx *context.Context) {
|
|||||||
var (
|
var (
|
||||||
owner = ctx.Params("username")
|
owner = ctx.Params("username")
|
||||||
filename = ctx.Req.Header.Get("filename")
|
filename = ctx.Req.Header.Get("filename")
|
||||||
email = ctx.Req.Header.Get("email")
|
|
||||||
distro = ctx.Req.Header.Get("distro")
|
distro = ctx.Req.Header.Get("distro")
|
||||||
sendtime = ctx.Req.Header.Get("time")
|
sign = ctx.Req.Header.Get("sign")
|
||||||
pkgsign = ctx.Req.Header.Get("pkgsign")
|
|
||||||
metasign = ctx.Req.Header.Get("metasign")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoding package signature.
|
|
||||||
sigdata, err := hex.DecodeString(pkgsign)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read package to memory for signature validation.
|
// Read package to memory for signature validation.
|
||||||
pkgdata, err := io.ReadAll(ctx.Req.Body)
|
pkgdata, err := io.ReadAll(ctx.Req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,33 +35,6 @@ func Push(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
defer ctx.Req.Body.Close()
|
defer ctx.Req.Body.Close()
|
||||||
|
|
||||||
// Get user and organization owning arch package.
|
|
||||||
user, org, err := arch_service.IdentifyOwner(ctx, owner, email)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoding time when message was created.
|
|
||||||
t, err := time.Parse(time.RFC3339, sendtime)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if message is outdated.
|
|
||||||
if time.Since(t) > time.Hour {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, "outdated message")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoding signature related to metadata.
|
|
||||||
msigdata, err := hex.DecodeString(metasign)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse metadata contained in arch package archive.
|
// Parse metadata contained in arch package archive.
|
||||||
md, err := arch_module.EjectMetadata(filename, distro, setting.Domain, pkgdata)
|
md, err := arch_module.EjectMetadata(filename, distro, setting.Domain, pkgdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -79,53 +42,40 @@ func Push(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validating metadata signature, to ensure that operation push operation
|
|
||||||
// is initiated by original package owner.
|
|
||||||
sendmetadata := []byte(owner + md.Name + sendtime)
|
|
||||||
err = arch_service.ValidateSignature(ctx, sendmetadata, msigdata, user)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate package signature with any of user's GnuPG keys.
|
|
||||||
err = arch_service.ValidateSignature(ctx, pkgdata, sigdata, user)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save file related to arch package.
|
// Save file related to arch package.
|
||||||
pkgid, err := arch_service.SaveFile(ctx, &arch_service.SaveFileParams{
|
pkgid, err := arch_service.SaveFile(ctx, &arch_service.SaveFileParams{
|
||||||
Organization: org,
|
Creator: ctx.Doer,
|
||||||
User: user,
|
Owner: ctx.Package.Owner,
|
||||||
Metadata: md,
|
Metadata: md,
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Data: pkgdata,
|
Data: pkgdata,
|
||||||
Distro: distro,
|
Distro: distro,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save file related to arch package signature.
|
// Decoding package signature, if present saving with package as file.
|
||||||
_, err = arch_service.SaveFile(ctx, &arch_service.SaveFileParams{
|
sigdata, err := hex.DecodeString(sign)
|
||||||
Organization: org,
|
if err == nil {
|
||||||
User: user,
|
_, err = arch_service.SaveFile(ctx, &arch_service.SaveFileParams{
|
||||||
Metadata: md,
|
Creator: ctx.Doer,
|
||||||
Data: sigdata,
|
Owner: ctx.Package.Owner,
|
||||||
Filename: filename + ".sig",
|
Metadata: md,
|
||||||
Distro: distro,
|
Data: sigdata,
|
||||||
})
|
Filename: filename + ".sig",
|
||||||
if err != nil {
|
Distro: distro,
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
})
|
||||||
return
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new architectures and distribution info to package version metadata.
|
// Add new architectures and distribution info to package version metadata.
|
||||||
err = arch_service.UpdateMetadata(ctx, &arch_service.UpdateMetadataParameters{
|
err = arch_service.UpdateMetadata(ctx, &arch_service.UpdateMetadataParameters{
|
||||||
User: org.AsUser(),
|
User: ctx.Package.Owner,
|
||||||
Md: md,
|
Md: md,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,50 +140,12 @@ func Get(ctx *context.Context) {
|
|||||||
// Remove specific package version, related files and pacman database entry.
|
// Remove specific package version, related files and pacman database entry.
|
||||||
func Remove(ctx *context.Context) {
|
func Remove(ctx *context.Context) {
|
||||||
var (
|
var (
|
||||||
owner = ctx.Params("username")
|
pkg = ctx.Req.Header.Get("package")
|
||||||
email = ctx.Req.Header.Get("email")
|
ver = ctx.Req.Header.Get("version")
|
||||||
target = ctx.Req.Header.Get("target")
|
|
||||||
stime = ctx.Req.Header.Get("time")
|
|
||||||
version = ctx.Req.Header.Get("version")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse sent time and check if it is within last minute.
|
|
||||||
t, err := time.Parse(time.RFC3339, stime)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if time.Since(t) > time.Minute {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, "outdated message")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user owning the package.
|
|
||||||
user, org, err := arch_service.IdentifyOwner(ctx, owner, email)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read signature data from request body.
|
|
||||||
sigdata, err := io.ReadAll(ctx.Req.Body)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ctx.Req.Body.Close()
|
|
||||||
|
|
||||||
// Validate package signature with any of user's GnuPG keys.
|
|
||||||
mesdata := []byte(owner + target + stime)
|
|
||||||
err = arch_service.ValidateSignature(ctx, mesdata, sigdata, user)
|
|
||||||
if err != nil {
|
|
||||||
apiError(ctx, http.StatusUnauthorized, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove package files and pacman database entry.
|
// Remove package files and pacman database entry.
|
||||||
err = arch_service.RemovePackage(ctx, org.AsUser(), target, version)
|
err := arch_service.RemovePackage(ctx, ctx.Package.Owner, pkg, ver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
|
||||||
pkg_model "code.gitea.io/gitea/models/packages"
|
pkg_model "code.gitea.io/gitea/models/packages"
|
||||||
"code.gitea.io/gitea/models/repo"
|
"code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/user"
|
"code.gitea.io/gitea/models/user"
|
||||||
@ -58,9 +57,9 @@ func UpdateMetadata(ctx *context.Context, p *UpdateMetadataParameters) error {
|
|||||||
|
|
||||||
// Parameters required to save new arch package.
|
// Parameters required to save new arch package.
|
||||||
type SaveFileParams struct {
|
type SaveFileParams struct {
|
||||||
*org_model.Organization
|
Creator *user.User
|
||||||
*user.User
|
Owner *user.User
|
||||||
*arch.Metadata
|
Metadata *arch.Metadata
|
||||||
Data []byte
|
Data []byte
|
||||||
Filename string
|
Filename string
|
||||||
Distro string
|
Distro string
|
||||||
@ -80,12 +79,12 @@ func SaveFile(ctx *context.Context, p *SaveFileParams) (int64, error) {
|
|||||||
pv, _, err := pkg_service.CreatePackageOrAddFileToExisting(
|
pv, _, err := pkg_service.CreatePackageOrAddFileToExisting(
|
||||||
&pkg_service.PackageCreationInfo{
|
&pkg_service.PackageCreationInfo{
|
||||||
PackageInfo: pkg_service.PackageInfo{
|
PackageInfo: pkg_service.PackageInfo{
|
||||||
Owner: p.Organization.AsUser(),
|
Owner: p.Owner,
|
||||||
PackageType: pkg_model.TypeArch,
|
PackageType: pkg_model.TypeArch,
|
||||||
Name: p.Metadata.Name,
|
Name: p.Metadata.Name,
|
||||||
Version: p.Metadata.Version,
|
Version: p.Metadata.Version,
|
||||||
},
|
},
|
||||||
Creator: p.User,
|
Creator: p.Creator,
|
||||||
Metadata: p.Metadata,
|
Metadata: p.Metadata,
|
||||||
},
|
},
|
||||||
&pkg_service.PackageFileCreationInfo{
|
&pkg_service.PackageFileCreationInfo{
|
||||||
@ -93,7 +92,7 @@ func SaveFile(ctx *context.Context, p *SaveFileParams) (int64, error) {
|
|||||||
Filename: p.Filename,
|
Filename: p.Filename,
|
||||||
CompositeKey: p.Distro + "-" + p.Filename,
|
CompositeKey: p.Distro + "-" + p.Filename,
|
||||||
},
|
},
|
||||||
Creator: p.User,
|
Creator: p.Creator,
|
||||||
Data: buf,
|
Data: buf,
|
||||||
OverwriteExisting: true,
|
OverwriteExisting: true,
|
||||||
IsLead: p.IsLead,
|
IsLead: p.IsLead,
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package arch
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/asymkey"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
org "code.gitea.io/gitea/models/organization"
|
|
||||||
"code.gitea.io/gitea/models/user"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
|
||||||
|
|
||||||
"github.com/keybase/go-crypto/openpgp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IdentidyOwnerParameters struct {
|
|
||||||
*context.Context
|
|
||||||
Owner string
|
|
||||||
Email string
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function will find user related to provided email address and check if
|
|
||||||
// he is able to push packages to provided namespace (user/organization/or
|
|
||||||
// empty namespace allowed for admin users). Function will return user making
|
|
||||||
// operation, organization or user owning the package.
|
|
||||||
func IdentifyOwner(ctx *context.Context, owner, email string) (*user.User, *org.Organization, error) {
|
|
||||||
u, err := user.GetUserByEmail(ctx, email)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to find user with email %s, %v", email, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if owner == "" && u.IsAdmin {
|
|
||||||
return u, (*org.Organization)(u), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if owner == u.Name {
|
|
||||||
return u, (*org.Organization)(u), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Name != owner {
|
|
||||||
org, err := org.GetOrgByName(ctx, owner)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to get organization: %s, %v", owner, err)
|
|
||||||
}
|
|
||||||
ismember, err := org.IsOrgMember(u.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("unable to check if user %s belongs to organization %s: %v", u.Name, org.Name, err)
|
|
||||||
}
|
|
||||||
if !ismember {
|
|
||||||
return nil, nil, fmt.Errorf("user %s is not member of organization %s", u.Name, org.Name)
|
|
||||||
}
|
|
||||||
return u, org, nil
|
|
||||||
}
|
|
||||||
return nil, nil, fmt.Errorf("unknown package owner")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate package signature with owner's GnuPG keys stored in gitea's database.
|
|
||||||
func ValidateSignature(ctx *context.Context, pkg, sign []byte, u *user.User) error {
|
|
||||||
keys, err := asymkey.ListGPGKeys(ctx, u.ID, db.ListOptions{
|
|
||||||
ListAll: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("unable to get public keys")
|
|
||||||
}
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return errors.New("no keys for user with email: " + u.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyarmors []string
|
|
||||||
for _, key := range keys {
|
|
||||||
k, err := asymkey.GetGPGImportByKeyID(key.KeyID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("unable to import GPG key armor")
|
|
||||||
}
|
|
||||||
keyarmors = append(keyarmors, k.Content)
|
|
||||||
}
|
|
||||||
|
|
||||||
var trace []error
|
|
||||||
for _, armor := range keyarmors {
|
|
||||||
kr, err := openpgp.ReadArmoredKeyRing(strings.NewReader(armor))
|
|
||||||
if err != nil {
|
|
||||||
trace = append(trace, fmt.Errorf("unable to get keys for %s: %v", u.Name, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = openpgp.CheckDetachedSignature(kr, bytes.NewReader(pkg), bytes.NewReader(sign))
|
|
||||||
if err != nil {
|
|
||||||
trace = append(trace, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Join(trace...)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user