diff --git a/services/packages/alpine/repository.go b/services/packages/alpine/repository.go index 146e9e4a028..3d2e4c29d7e 100644 --- a/services/packages/alpine/repository.go +++ b/services/packages/alpine/repository.go @@ -44,32 +44,9 @@ func GetOrCreateRepositoryVersion(ctx context.Context, ownerID int64) (*packages // GetOrCreateKeyPair gets or creates the RSA keys used to sign repository files func GetOrCreateKeyPair(ctx context.Context, ownerID int64) (string, string, error) { - priv, err := user_model.GetSetting(ctx, ownerID, alpine_module.SettingKeyPrivate) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - pub, err := user_model.GetSetting(ctx, ownerID, alpine_module.SettingKeyPublic) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - if priv == "" || pub == "" { - priv, pub, err = util.GenerateKeyPair(4096) - if err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, alpine_module.SettingKeyPrivate, priv); err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, alpine_module.SettingKeyPublic, pub); err != nil { - return "", "", err - } - } - - return priv, pub, nil + return packages_service.GetOrCreateKeyPair(ctx, ownerID, alpine_module.SettingKeyPrivate, alpine_module.SettingKeyPublic, func() (string, string, error) { + return util.GenerateKeyPair(4096) + }) } // BuildAllRepositoryFiles (re)builds all repository files for every available branches, repositories and architectures diff --git a/services/packages/arch/repository.go b/services/packages/arch/repository.go index 1943c5f21f1..7aa22be2535 100644 --- a/services/packages/arch/repository.go +++ b/services/packages/arch/repository.go @@ -49,32 +49,7 @@ func GetOrCreateRepositoryVersion(ctx context.Context, ownerID int64) (*packages // GetOrCreateKeyPair gets or creates the PGP keys used to sign repository files func GetOrCreateKeyPair(ctx context.Context, ownerID int64) (string, string, error) { - priv, err := user_model.GetSetting(ctx, ownerID, arch_module.SettingKeyPrivate) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - pub, err := user_model.GetSetting(ctx, ownerID, arch_module.SettingKeyPublic) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - if priv == "" || pub == "" { - priv, pub, err = generateKeypair() - if err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, arch_module.SettingKeyPrivate, priv); err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, arch_module.SettingKeyPublic, pub); err != nil { - return "", "", err - } - } - - return priv, pub, nil + return packages_service.GetOrCreateKeyPair(ctx, ownerID, arch_module.SettingKeyPrivate, arch_module.SettingKeyPublic, generateKeypair) } func generateKeypair() (string, string, error) { diff --git a/services/packages/debian/repository.go b/services/packages/debian/repository.go index 7073d44e3e1..a635dea1be4 100644 --- a/services/packages/debian/repository.go +++ b/services/packages/debian/repository.go @@ -39,32 +39,7 @@ func GetOrCreateRepositoryVersion(ctx context.Context, ownerID int64) (*packages // GetOrCreateKeyPair gets or creates the PGP keys used to sign repository files func GetOrCreateKeyPair(ctx context.Context, ownerID int64) (string, string, error) { - priv, err := user_model.GetSetting(ctx, ownerID, debian_module.SettingKeyPrivate) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - pub, err := user_model.GetSetting(ctx, ownerID, debian_module.SettingKeyPublic) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - if priv == "" || pub == "" { - priv, pub, err = generateKeypair() - if err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, debian_module.SettingKeyPrivate, priv); err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, debian_module.SettingKeyPublic, pub); err != nil { - return "", "", err - } - } - - return priv, pub, nil + return packages_service.GetOrCreateKeyPair(ctx, ownerID, debian_module.SettingKeyPrivate, debian_module.SettingKeyPublic, generateKeypair) } func generateKeypair() (string, string, error) { diff --git a/services/packages/keypair.go b/services/packages/keypair.go new file mode 100644 index 00000000000..724fd41b748 --- /dev/null +++ b/services/packages/keypair.go @@ -0,0 +1,57 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package packages + +import ( + "context" + "errors" + "fmt" + + user_model "gitea.dev/models/user" + "gitea.dev/modules/globallock" + "gitea.dev/modules/util" +) + +// GetOrCreateKeyPair gets the owner's key pair used to sign repository files, +// generating and storing it if it does not exist yet. +func GetOrCreateKeyPair(ctx context.Context, ownerID int64, settingKeyPriv, settingKeyPub string, generate func() (priv, pub string, err error)) (string, string, error) { + priv, pub, err := getKeyPair(ctx, ownerID, settingKeyPriv, settingKeyPub) + if err != nil || (priv != "" && pub != "") { + return priv, pub, err + } + + err = globallock.LockAndDo(ctx, fmt.Sprintf("pkg-keypair-%s-%d", settingKeyPriv, ownerID), func(ctx context.Context) error { + priv, pub, err = getKeyPair(ctx, ownerID, settingKeyPriv, settingKeyPub) // re-read inside the lock, another request may have created it + if err != nil || (priv != "" && pub != "") { + return err + } + + if priv, pub, err = generate(); err != nil { + return err + } + + if err := user_model.SetUserSetting(ctx, ownerID, settingKeyPriv, priv); err != nil { + return err + } + return user_model.SetUserSetting(ctx, ownerID, settingKeyPub, pub) + }) + if err != nil { + return "", "", err + } + return priv, pub, nil +} + +func getKeyPair(ctx context.Context, ownerID int64, settingKeyPriv, settingKeyPub string) (string, string, error) { + priv, err := user_model.GetSetting(ctx, ownerID, settingKeyPriv) + if err != nil && !errors.Is(err, util.ErrNotExist) { + return "", "", err + } + + pub, err := user_model.GetSetting(ctx, ownerID, settingKeyPub) + if err != nil && !errors.Is(err, util.ErrNotExist) { + return "", "", err + } + + return priv, pub, nil +} diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go index 5ea08bf0631..3be1bcc3b12 100644 --- a/services/packages/rpm/repository.go +++ b/services/packages/rpm/repository.go @@ -10,7 +10,6 @@ import ( "crypto/sha256" "encoding/hex" "encoding/xml" - "errors" "fmt" "io" "slices" @@ -23,7 +22,6 @@ import ( "gitea.dev/modules/json" packages_module "gitea.dev/modules/packages" rpm_module "gitea.dev/modules/packages/rpm" - "gitea.dev/modules/util" packages_service "gitea.dev/services/packages" "github.com/ProtonMail/go-crypto/openpgp" @@ -39,32 +37,7 @@ func GetOrCreateRepositoryVersion(ctx context.Context, ownerID int64) (*packages // GetOrCreateKeyPair gets or creates the PGP keys used to sign repository metadata files func GetOrCreateKeyPair(ctx context.Context, ownerID int64) (string, string, error) { - priv, err := user_model.GetSetting(ctx, ownerID, rpm_module.SettingKeyPrivate) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - pub, err := user_model.GetSetting(ctx, ownerID, rpm_module.SettingKeyPublic) - if err != nil && !errors.Is(err, util.ErrNotExist) { - return "", "", err - } - - if priv == "" || pub == "" { - priv, pub, err = generateKeypair() - if err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, rpm_module.SettingKeyPrivate, priv); err != nil { - return "", "", err - } - - if err := user_model.SetUserSetting(ctx, ownerID, rpm_module.SettingKeyPublic, pub); err != nil { - return "", "", err - } - } - - return priv, pub, nil + return packages_service.GetOrCreateKeyPair(ctx, ownerID, rpm_module.SettingKeyPrivate, rpm_module.SettingKeyPublic, generateKeypair) } func generateKeypair() (string, string, error) {