2023-06-21 20:30:07 +00:00
|
|
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package arch
|
|
|
|
|
|
|
|
import (
|
2023-06-23 00:59:28 +00:00
|
|
|
"bytes"
|
2023-06-21 20:30:07 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2023-06-23 00:59:28 +00:00
|
|
|
"strings"
|
2023-06-21 20:30:07 +00:00
|
|
|
|
|
|
|
"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"
|
2023-06-23 00:59:28 +00:00
|
|
|
"github.com/keybase/go-crypto/openpgp"
|
2023-06-21 20:30:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type IdentidyOwnerParameters struct {
|
|
|
|
*context.Context
|
|
|
|
Owner string
|
|
|
|
Email string
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function will find user related to provided email adress 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.
|
2023-06-25 10:15:29 +00:00
|
|
|
func ValidateSignature(ctx *context.Context, pkg, sign []byte, u *user.User) error {
|
2023-06-21 20:30:07 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-06-23 00:59:28 +00:00
|
|
|
var trace []error
|
2023-06-21 20:30:07 +00:00
|
|
|
for _, armor := range keyarmors {
|
2023-06-23 00:59:28 +00:00
|
|
|
kr, err := openpgp.ReadArmoredKeyRing(strings.NewReader(armor))
|
2023-06-21 20:30:07 +00:00
|
|
|
if err != nil {
|
2023-06-23 00:59:28 +00:00
|
|
|
trace = append(trace, fmt.Errorf("unable to get keys for %s: %v", u.Name, err))
|
|
|
|
continue
|
2023-06-21 20:30:07 +00:00
|
|
|
}
|
2023-06-23 00:59:28 +00:00
|
|
|
_, err = openpgp.CheckDetachedSignature(kr, bytes.NewReader(pkg), bytes.NewReader(sign))
|
2023-06-21 20:30:07 +00:00
|
|
|
if err != nil {
|
2023-06-23 00:59:28 +00:00
|
|
|
trace = append(trace, err)
|
|
|
|
continue
|
2023-06-21 20:30:07 +00:00
|
|
|
}
|
2023-06-23 00:59:28 +00:00
|
|
|
return nil
|
2023-06-21 20:30:07 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 00:59:28 +00:00
|
|
|
return errors.Join(trace...)
|
2023-06-21 20:30:07 +00:00
|
|
|
}
|