From 6b24adaf8348d637d73f85f62c3e10a7d954f436 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Tue, 5 Jan 2021 17:46:37 +0200 Subject: [PATCH 01/80] Add simple master key provider for secret encryption --- cmd/generate.go | 51 +++++++++++++++++++++++++ modules/generate/generate.go | 21 ++++++++++ modules/setting/setting.go | 16 ++++++++ modules/web/middleware/binding.go | 7 ++++ options/locale/locale_en-US.ini | 8 ++++ routers/install/install.go | 34 +++++++++++++++++ services/forms/user_form.go | 2 + services/secrets/masterkey.go | 27 +++++++++++++ services/secrets/masterkey_nop.go | 43 +++++++++++++++++++++ services/secrets/masterkey_plain.go | 59 +++++++++++++++++++++++++++++ services/secrets/secrets.go | 42 ++++++++++++++++++++ templates/install.tmpl | 16 ++++++++ 12 files changed, 326 insertions(+) create mode 100644 services/secrets/masterkey.go create mode 100644 services/secrets/masterkey_nop.go create mode 100644 services/secrets/masterkey_plain.go create mode 100644 services/secrets/secrets.go diff --git a/cmd/generate.go b/cmd/generate.go index 35c77a815b1d9..aaf1fe7020d22 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -6,10 +6,14 @@ package cmd import ( + "encoding/base64" "fmt" "os" "code.gitea.io/gitea/modules/generate" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/secrets" "github.com/mattn/go-isatty" "github.com/urfave/cli" @@ -32,6 +36,7 @@ var ( microcmdGenerateInternalToken, microcmdGenerateLfsJwtSecret, microcmdGenerateSecretKey, + microcmdGenerateMasterKey, }, } @@ -53,6 +58,12 @@ var ( Usage: "Generate a new SECRET_KEY", Action: runGenerateSecretKey, } + + microcmdGenerateMasterKey = cli.Command{ + Name: "MASTER_KEY", + Usage: "Generate a new MASTER_KEY", + Action: runGenerateMasterKey, + } ) func runGenerateInternalToken(c *cli.Context) error { @@ -99,3 +110,43 @@ func runGenerateSecretKey(c *cli.Context) error { return nil } + +func runGenerateMasterKey(c *cli.Context) error { + // Silence the console logger + log.DelNamedLogger("console") + log.DelNamedLogger(log.DEFAULT) + + // Read configuration file + setting.LoadFromExisting() + + providerType := secrets.MasterKeyProviderType(setting.MasterKeyProvider) + if providerType == secrets.MasterKeyProviderTypeNone { + return fmt.Errorf("configured master key provider does not support key generation") + } + + if err := secrets.Init(); err != nil { + return err + } + + scrts, err := secrets.GenerateMasterKey() + if err != nil { + return err + } + + if len(scrts) > 1 { + fmt.Println("Unseal secrets:") + for i, secret := range scrts { + if i > 0 { + fmt.Printf("\n") + } + fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(secret)) + } + } + fmt.Println("Setting changes required:") + fmt.Println("[secrets]") + if providerType == secrets.MasterKeyProviderTypePlain && len(scrts) == 1 { + fmt.Printf("MASTER_KEY = %s\n", base64.StdEncoding.EncodeToString(scrts[0])) + } + + return nil +} diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 326fe8036b445..c28b99d552160 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "encoding/base64" "io" + "math/big" "time" "code.gitea.io/gitea/modules/util" @@ -67,3 +68,23 @@ func NewSecretKey() (string, error) { return secretKey, nil } + +// NewMasterKey generate a new value intended to be used by MASTER_KEY. +func NewMasterKey() ([]byte, error) { + secretBytes := make([]byte, 32) + _, err := io.ReadFull(rand.Reader, secretBytes) + if err != nil { + return nil, err + } + + return secretBytes, nil +} + +func randomInt(max *big.Int) (int, error) { + rand, err := rand.Int(rand.Reader, max) + if err != nil { + return 0, err + } + + return int(rand.Int64()), nil +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 6233437bf5aac..645a66fd8b3b2 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -213,6 +213,8 @@ var ( HMACKey string `ini:"HMAC_KEY"` Allways bool }{} + MasterKeyProvider string + MasterKey []byte // UI settings UI = struct { @@ -953,6 +955,20 @@ func loadFromConf(allowEmpty bool, extraConfig string) { PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) + // Master key provider configuration + MasterKeyProvider = sec.Key("MASTER_KEY_PROVIDER").MustString("none") + switch MasterKeyProvider { + case "plain": + if MasterKey, err = base64.StdEncoding.DecodeString(sec.Key("MASTER_KEY").MustString("")); err != nil { + log.Fatal("error loading master key: %v", err) + return + } + case "none": + default: + log.Fatal("invalid master key provider type: %v", MasterKeyProvider) + return + } + InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 636e655b9e956..d87b26b793df7 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -79,6 +79,11 @@ func GetInclude(field reflect.StructField) string { return getRuleBody(field, "Include(") } +// GetIn get allowed values in form tag +func GetIn(field reflect.StructField) string { + return getRuleBody(field, "In(") +} + // Validate validate TODO: func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors { if errs.Len() == 0 { @@ -131,6 +136,8 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl data["ErrorMsg"] = trName + l.Tr("form.url_error", errs[0].Message) case binding.ERR_INCLUDE: data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) + case binding.ERR_IN: + data["ErrorMsg"] = trName + l.Tr("form.in_error", strings.Join(strings.Split(GetIn(field), ","), ", ")) case validation.ErrGlobPattern: data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) case validation.ErrRegexPattern: diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1dba1d71d8ffe..38f27e70065fe 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -176,6 +176,12 @@ app_url_helper = Base address for HTTP(S) clone URLs and email notifications. log_root_path = Log Path log_root_path_helper = Log files will be written to this directory. +security_title = Security Settings +master_key_provider = Master Key Provider +master_key_provider_none = None +master_key_provider_plain = Plain +master_key_provider_helper = Master Key Provider to use to store secret key that will be used for other secret encryption. Use "None" to not encrypt secrets. Use "Plain" to store automatically generated secret in configuration file. + optional_title = Optional Settings email_title = Email Settings smtp_addr = SMTP Host @@ -234,6 +240,7 @@ no_reply_address = Hidden Email Domain no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. password_algorithm = Password Hash Algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. `argon2` whilst having good characteristics uses a lot of memory and may be inappropriate for small systems. +master_key_failed = Failed to generate master key: %v [home] uname_holder = Username or Email Address @@ -447,6 +454,7 @@ max_size_error = ` must contain at most %s characters.` email_error = ` is not a valid email address.` url_error = `'%s' is not a valid URL.` include_error = ` must contain substring '%s'.` +in_error = ` can contain only specific values: %s.` glob_pattern_error = ` glob pattern is invalid: %s.` regex_pattern_error = ` regex pattern is invalid: %s.` unknown_error = Unknown error: diff --git a/routers/install/install.go b/routers/install/install.go index 890725b9a747f..310f0a6bc6662 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -7,6 +7,7 @@ package install import ( goctx "context" + "encoding/base64" "fmt" "net/http" "os" @@ -32,6 +33,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/forms" + "code.gitea.io/gitea/services/secrets" "gitea.com/go-chi/session" "gopkg.in/ini.v1" @@ -160,6 +162,7 @@ func Install(ctx *context.Context) { form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress form.PasswordAlgorithm = setting.PasswordHashAlgo + form.MasterKeyProvider = secrets.MasterKeyProviderTypePlain middleware.AssignForm(form, ctx.Data) ctx.HTML(http.StatusOK, tplInstall) @@ -386,10 +389,40 @@ func SubmitInstall(ctx *context.Context) { log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err) } } + + // Setup master key provider + cfg.Section("security").Key("MASTER_KEY_PROVIDER").SetValue(string(form.MasterKeyProvider)) + var provider secrets.MasterKeyProvider + switch form.MasterKeyProvider { + case secrets.MasterKeyProviderTypePlain: + provider = secrets.NewPlainMasterKeyProvider() + } + var masterKey []byte + if provider != nil { + if err = provider.Init(); err != nil { + ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) + return + } + // Generate master key + if _, err = provider.GenerateMasterKey(); err != nil { + ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) + return + } + masterKey, err = provider.GetMasterKey() + if err != nil { + ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) + return + } + if form.MasterKeyProvider == secrets.MasterKeyProviderTypePlain { + cfg.Section("security").Key("MASTER_KEY").SetValue(base64.StdEncoding.EncodeToString(masterKey)) + } + } + cfg.Section("database").Key("DB_TYPE").SetValue(setting.Database.Type) cfg.Section("database").Key("HOST").SetValue(setting.Database.Host) cfg.Section("database").Key("NAME").SetValue(setting.Database.Name) cfg.Section("database").Key("USER").SetValue(setting.Database.User) + // TODO: Encrypt secret cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd) cfg.Section("database").Key("SCHEMA").SetValue(setting.Database.Schema) cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode) @@ -431,6 +464,7 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom) cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser) + // TODO: Encrypt secret cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) } else { cfg.Section("mailer").Key("ENABLED").SetValue("false") diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 8ce1d85c57781..27d68c50b5c9a 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/secrets" "gitea.com/go-chi/binding" ) @@ -63,6 +64,7 @@ type InstallForm struct { NoReplyAddress string PasswordAlgorithm string + MasterKeyProvider secrets.MasterKeyProviderType `binding:"Required;In(none,plain)"` AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"` AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"` diff --git a/services/secrets/masterkey.go b/services/secrets/masterkey.go new file mode 100644 index 0000000000000..c69a75b78ab98 --- /dev/null +++ b/services/secrets/masterkey.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +import ( + "fmt" +) + +// ErrMasterKeySealed is returned when trying to use master key that is sealed +var ErrMasterKeySealed = fmt.Errorf("master key sealed") + +// MasterKeyProvider provides master key used for encryption +type MasterKeyProvider interface { + Init() error + + GenerateMasterKey() ([][]byte, error) + + Unseal(secret []byte) error + + Seal() error + + IsSealed() bool + + GetMasterKey() ([]byte, error) +} diff --git a/services/secrets/masterkey_nop.go b/services/secrets/masterkey_nop.go new file mode 100644 index 0000000000000..83d85ab2769c2 --- /dev/null +++ b/services/secrets/masterkey_nop.go @@ -0,0 +1,43 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +type nopMasterKeyProvider struct { +} + +// NewNopMasterKeyProvider returns master key provider that holds no master key and is always unsealed +func NewNopMasterKeyProvider() MasterKeyProvider { + return &nopMasterKeyProvider{} +} + +// Init initializes master key provider +func (k *nopMasterKeyProvider) Init() error { + return nil +} + +// GenerateMasterKey always returns empty master key +func (k *nopMasterKeyProvider) GenerateMasterKey() ([][]byte, error) { + return nil, nil +} + +// Unseal master key by providing unsealing secret +func (k *nopMasterKeyProvider) Unseal(secret []byte) error { + return nil +} + +// Seal master key +func (k *nopMasterKeyProvider) Seal() error { + return nil +} + +// IsSealed always returns false +func (k *nopMasterKeyProvider) IsSealed() bool { + return false +} + +// GetMasterKey returns empty master key +func (k *nopMasterKeyProvider) GetMasterKey() ([]byte, error) { + return nil, nil +} diff --git a/services/secrets/masterkey_plain.go b/services/secrets/masterkey_plain.go new file mode 100644 index 0000000000000..6a457c745acfb --- /dev/null +++ b/services/secrets/masterkey_plain.go @@ -0,0 +1,59 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +import ( + "code.gitea.io/gitea/modules/generate" + "code.gitea.io/gitea/modules/setting" +) + +type plainMasterKeyProvider struct { + key []byte +} + +// NewPlainMasterKeyProvider returns unsecured static master key provider +func NewPlainMasterKeyProvider() MasterKeyProvider { + return &plainMasterKeyProvider{} +} + +// Init initializes master key provider +func (k *plainMasterKeyProvider) Init() error { + return k.Unseal(nil) +} + +// GenerateMasterKey generates a new master key and returns secret or secrets for unsealing +func (k *plainMasterKeyProvider) GenerateMasterKey() ([][]byte, error) { + key, err := generate.NewMasterKey() + if err != nil { + return nil, err + } + k.key = key + return [][]byte{key}, nil +} + +// Unseal master key by providing unsealing secret +func (k *plainMasterKeyProvider) Unseal(secret []byte) error { + k.key = setting.MasterKey + return nil +} + +// Seal master key +func (k *plainMasterKeyProvider) Seal() error { + k.key = nil + return nil +} + +// IsSealed returns if master key is sealed +func (k *plainMasterKeyProvider) IsSealed() bool { + return len(k.key) == 0 +} + +// GetMasterKey returns master key +func (k *plainMasterKeyProvider) GetMasterKey() ([]byte, error) { + if k.IsSealed() { + return nil, ErrMasterKeySealed + } + return k.key, nil +} diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go new file mode 100644 index 0000000000000..accaabea2ca18 --- /dev/null +++ b/services/secrets/secrets.go @@ -0,0 +1,42 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +import ( + "fmt" + + "code.gitea.io/gitea/modules/setting" +) + +// MasterKeyProviderType is the type of master key provider +type MasterKeyProviderType string + +// Types of master key providers +const ( + MasterKeyProviderTypeNone MasterKeyProviderType = "none" + MasterKeyProviderTypePlain MasterKeyProviderType = "plain" +) + +var ( + masterKey MasterKeyProvider +) + +// Init initializes master key provider based on settings +func Init() error { + switch MasterKeyProviderType(setting.MasterKeyProvider) { + case MasterKeyProviderTypeNone: + masterKey = NewNopMasterKeyProvider() + case MasterKeyProviderTypePlain: + masterKey = NewPlainMasterKeyProvider() + default: + return fmt.Errorf("invalid master key provider %v", setting.MasterKeyProvider) + } + return nil +} + +// GenerateMasterKey generates a new master key and returns secret or secrets for unsealing +func GenerateMasterKey() ([][]byte, error) { + return masterKey.GenerateMasterKey() +} diff --git a/templates/install.tmpl b/templates/install.tmpl index 36f58218d4638..b3cfa005f88dd 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -163,6 +163,22 @@ {{.locale.Tr "install.log_root_path_helper"}} + +

{{.i18n.Tr "install.security_title"}}

+ +
+ + + {{.i18n.Tr "install.master_key_provider_helper"}} +

{{.locale.Tr "install.optional_title"}}

From 47c472a265084b38db3dca4d3311f356036f3b4f Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Thu, 7 Jan 2021 11:31:54 +0200 Subject: [PATCH 02/80] Add AES GCM encryption provider --- cmd/generate.go | 9 ++- services/secrets/encryption.go | 16 +++++ services/secrets/encryption_aes.go | 89 ++++++++++++++++++++++++++ services/secrets/masterkey_nop_test.go | 12 ++++ services/secrets/secrets.go | 58 ++++++++++++++++- 5 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 services/secrets/encryption.go create mode 100644 services/secrets/encryption_aes.go create mode 100644 services/secrets/masterkey_nop_test.go diff --git a/cmd/generate.go b/cmd/generate.go index aaf1fe7020d22..629c948eda3e4 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -142,10 +142,13 @@ func runGenerateMasterKey(c *cli.Context) error { fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(secret)) } } - fmt.Println("Setting changes required:") - fmt.Println("[secrets]") + if providerType == secrets.MasterKeyProviderTypePlain && len(scrts) == 1 { - fmt.Printf("MASTER_KEY = %s\n", base64.StdEncoding.EncodeToString(scrts[0])) + fmt.Printf("%s", base64.StdEncoding.EncodeToString(scrts[0])) + + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Printf("\n") + } } return nil diff --git a/services/secrets/encryption.go b/services/secrets/encryption.go new file mode 100644 index 0000000000000..4cf192ee61e36 --- /dev/null +++ b/services/secrets/encryption.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +// EncryptionProvider encrypts and decrypts secrets +type EncryptionProvider interface { + Encrypt(secret, key []byte) ([]byte, error) + + EncryptString(secret string, key []byte) (string, error) + + Decrypt(enc, key []byte) ([]byte, error) + + DecryptString(enc string, key []byte) (string, error) +} diff --git a/services/secrets/encryption_aes.go b/services/secrets/encryption_aes.go new file mode 100644 index 0000000000000..d34636c7aa677 --- /dev/null +++ b/services/secrets/encryption_aes.go @@ -0,0 +1,89 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package secrets + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "fmt" + "io" +) + +type aesEncryptionProvider struct { +} + +func NewAesEncryptionProvider() EncryptionProvider { + return &aesEncryptionProvider{} +} + +func (e *aesEncryptionProvider) Encrypt(secret, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + c, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := make([]byte, c.NonceSize(), c.NonceSize()+c.Overhead()+len(secret)) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + out := c.Seal(nil, nonce, secret, nil) + + return append(nonce, out...), nil +} + +func (e *aesEncryptionProvider) EncryptString(secret string, key []byte) (string, error) { + out, err := e.Encrypt([]byte(secret), key) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(out), nil +} + +func (e *aesEncryptionProvider) Decrypt(enc, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + c, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + if len(enc) < c.NonceSize() { + return nil, fmt.Errorf("encrypted value too short") + } + + nonce := enc[:c.NonceSize()] + ciphertext := enc[c.NonceSize():] + + out, err := c.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + return out, nil +} + +func (e *aesEncryptionProvider) DecryptString(enc string, key []byte) (string, error) { + encb, err := base64.StdEncoding.DecodeString(enc) + if err != nil { + return "", err + } + + out, err := e.Encrypt(encb, key) + if err != nil { + return "", err + } + + return string(out), nil +} diff --git a/services/secrets/masterkey_nop_test.go b/services/secrets/masterkey_nop_test.go new file mode 100644 index 0000000000000..09e4587d4dfd1 --- /dev/null +++ b/services/secrets/masterkey_nop_test.go @@ -0,0 +1,12 @@ +package secrets + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNopMasterKey_IsSealed(t *testing.T) { + k := NewNopMasterKeyProvider() + assert.False(t, k.IsSealed()) +} diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index accaabea2ca18..045985578113c 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -20,7 +20,8 @@ const ( ) var ( - masterKey MasterKeyProvider + masterKey MasterKeyProvider + encProvider EncryptionProvider ) // Init initializes master key provider based on settings @@ -33,6 +34,9 @@ func Init() error { default: return fmt.Errorf("invalid master key provider %v", setting.MasterKeyProvider) } + + encProvider = NewAesEncryptionProvider() + return nil } @@ -40,3 +44,55 @@ func Init() error { func GenerateMasterKey() ([][]byte, error) { return masterKey.GenerateMasterKey() } + +func Encrypt(secret []byte) ([]byte, error) { + key, err := masterKey.GetMasterKey() + if err != nil { + return nil, err + } + + if len(key) == 0 { + return secret, nil + } + + return encProvider.Encrypt(secret, key) +} + +func EncryptString(secret string) (string, error) { + key, err := masterKey.GetMasterKey() + if err != nil { + return "", err + } + + if len(key) == 0 { + return secret, nil + } + + return encProvider.EncryptString(secret, key) +} + +func Decrypt(enc []byte) ([]byte, error) { + key, err := masterKey.GetMasterKey() + if err != nil { + return nil, err + } + + if len(key) == 0 { + return enc, nil + } + + return encProvider.Decrypt(enc, key) +} + +func DecryptString(enc string) (string, error) { + key, err := masterKey.GetMasterKey() + if err != nil { + return "", err + } + + if len(key) == 0 { + return enc, nil + } + + return encProvider.DecryptString(enc, key) +} From 64a91b45474281e78fe5201432a3a982bbed8931 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 7 Oct 2022 22:43:56 +0800 Subject: [PATCH 03/80] add models and services --- models/auth/secret.go | 54 ++++++++++++++++++ models/db/search.go | 17 ++++++ routers/web/repo/setting.go | 8 +++ services/secrets/secrets.go | 47 +++++++++++++++ templates/repo/settings/deploy_keys.tmpl | 2 + templates/repo/settings/tokens.tmpl | 73 ++++++++++++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 models/auth/secret.go create mode 100644 templates/repo/settings/tokens.tmpl diff --git a/models/auth/secret.go b/models/auth/secret.go new file mode 100644 index 0000000000000..32b1c48e9ccb8 --- /dev/null +++ b/models/auth/secret.go @@ -0,0 +1,54 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package auth + +import ( + "fmt" + "regexp" + + "code.gitea.io/gitea/modules/timeutil" +) + +type ErrSecretNameInvalid struct { + Name string +} + +func (err ErrSecretNameInvalid) Error() string { + return fmt.Sprintf("secret name %s is invalid", err.Name) +} + +type ErrSecretDataInvalid struct { + Data string +} + +func (err ErrSecretDataInvalid) Error() string { + return fmt.Sprintf("secret data %s is invalid", err.Data) +} + +var nameRE = regexp.MustCompile("[^a-zA-Z0-9-_.]+") + +type Secret struct { + ID int64 + UserID int64 `xorm:"index"` + RepoID int64 `xorm:"index"` + Name string + Data string + PullRequest bool + CreatedUnix timeutil.TimeStamp +} + + // Validate validates the required fields and formats. +func (s *Secret) Validate() error { + switch { + case len(s.Name) == 0: + return ErrSecretNameInvalid{Name: s.Name} + case len(s.Data) == 0: + return ErrSecretDataInvalid{Data: s.Data} + case nameRE.MatchString(s.Name): + return ErrSecretNameInvalid{Name: s.Name} + default: + return nil + } +} diff --git a/models/db/search.go b/models/db/search.go index 704a48ed1eb40..f4bbd9d3fafa2 100644 --- a/models/db/search.go +++ b/models/db/search.go @@ -4,6 +4,12 @@ package db +import ( + "context" + + "xorm.io/builder" +) + // SearchOrderBy is used to sort the result type SearchOrderBy string @@ -28,3 +34,14 @@ const ( SearchOrderByForks SearchOrderBy = "num_forks ASC" SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" ) + +func FindObjects[Object any](ctx context.Context, cond builder.Cond, opts *ListOptions, objects *[]*Object) error { + sess := GetEngine(ctx).Where(cond) + if opts != nil && opts.PageSize > 0 { + if opts.Page < 1 { + opts.Page = 1 + } + sess.Limit(opts.PageSize, opts.PageSize * (opts.Page - 1)) + } + return sess.Find(objects) +} diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index e7abec0d3e895..db1c1ed00017c 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -46,6 +46,7 @@ import ( org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" + secret_service "code.gitea.io/gitea/services/secrets" ) const ( @@ -1104,6 +1105,13 @@ func DeployKeys(ctx *context.Context) { } ctx.Data["Deploykeys"] = keys + tokens, err := secret_service.FindRepoSecrets(ctx, ctx.Repo.Repository.ID) + if err != nil { + ctx.ServerError("FindRepoSecrets", err) + return + } + ctx.Data["Tokens"] = tokens + ctx.HTML(http.StatusOK, tplDeployKeys) } diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 045985578113c..4f113958d42fd 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -5,9 +5,14 @@ package secrets import ( + "context" "fmt" + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/setting" + + "xorm.io/builder" ) // MasterKeyProviderType is the type of master key provider @@ -96,3 +101,45 @@ func DecryptString(enc string) (string, error) { return encProvider.DecryptString(enc, key) } + +func InsertRepoSecret(ctx context.Context, repoID int64, key, data string, pullRequest bool) error { + v, err := EncryptString( data) + if err != nil { + return err + } +return db.Insert(ctx, &auth_model.Secret{ + RepoID: repoID, + Name: key, + Data: v, + PullRequest: pullRequest, +}) +} + +func InsertOrgSecret(ctx context.Context, userID int64, key, data string, pullRequest bool) error { + v, err := EncryptString(data) + if err != nil { + return err + } +return db.Insert(ctx, &auth_model.Secret{ + UserID: userID, + Name: key, + Data: v, + PullRequest: pullRequest, +}) +} + +func DeleteSecretByID(ctx context.Context, id int64) error { +_, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}) +return err +} + + +func FindRepoSecrets(ctx context.Context,repoID int64) ([]*auth_model.Secret, error) { + var res []*auth_model.Secret + return res, db.FindObjects(ctx, builder.Eq{"repo_id": repoID}, nil,&res) +} + +func FindUserSecrets(ctx context.Context, userID int64) ([]*auth_model.Secret, error) { + var res []*auth_model.Secret + return res, db.FindObjects(ctx, builder.Eq{"user_id": userID}, nil,&res) +} diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 2f8a4c6c1e25b..1d782e18c9cb5 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -75,6 +75,8 @@ {{end}} + + {{template "settings/tokens" .}} - - {{template "settings/tokens" .}} +
+ {{template "repo/settings/secrets" .}} From acc0c124e88a1326d1d30f7f96a0f205b8f1a3ee Mon Sep 17 00:00:00 2001 From: Jason Song Date: Fri, 9 Dec 2022 18:21:16 +0800 Subject: [PATCH 25/80] fix: FindUserSecrets --- routers/web/org/setting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 0a38490e1bfdd..751f04e661e13 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -259,7 +259,7 @@ func Secrets(ctx *context.Context) { secrets, err := secret_service.FindUserSecrets(ctx, ctx.Org.Organization.ID) if err != nil { - ctx.ServerError("FindRepoSecrets", err) + ctx.ServerError("FindUserSecrets", err) return } ctx.Data["Secrets"] = secrets From 3183368aa6a1d5ad0e1e2e2597b659444cfc2737 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:22:28 +0800 Subject: [PATCH 26/80] Update templates/org/settings/navbar.tmpl Co-authored-by: delvh --- templates/org/settings/navbar.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/org/settings/navbar.tmpl b/templates/org/settings/navbar.tmpl index c7a81f3a4057a..9ac45a855c731 100644 --- a/templates/org/settings/navbar.tmpl +++ b/templates/org/settings/navbar.tmpl @@ -12,7 +12,7 @@ {{.locale.Tr "repo.labels"}} - + {{.locale.Tr "org.settings.secrets"}} {{if .EnableOAuth2}} From e86e30ff30cf01f933623107d41a9983ccc92234 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:49:37 +0800 Subject: [PATCH 27/80] Update options/locale/locale_en-US.ini Co-authored-by: delvh --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d71534679bcf7..4d8640e6b945b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2071,7 +2071,7 @@ settings.secrets = Secrets settings.add_secret = Add Secret settings.add_secret_success = The secret '%s' has been added. settings.secret_value_content_placeholder = Input any content -settings.secret_desc = Secrets could be visited by repository events +settings.secret_desc = Secrets will be passed to certain actions and cannot be read otherwise. settings.secret_content = Value settings.secret_key = Key settings.no_secret = There are no secrets yet. From e6cee41a48aa0e0609e19e05665633cb7fa0c46d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:57:34 +0800 Subject: [PATCH 28/80] Update options/locale/locale_en-US.ini Co-authored-by: delvh --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 4d8640e6b945b..105dee9cdddec 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2073,7 +2073,7 @@ settings.add_secret_success = The secret '%s' has been added. settings.secret_value_content_placeholder = Input any content settings.secret_desc = Secrets will be passed to certain actions and cannot be read otherwise. settings.secret_content = Value -settings.secret_key = Key +settings.secret_name = Name settings.no_secret = There are no secrets yet. settings.secret_deletion = Remove secret settings.secret_deletion_desc = Removing a secret will revoke its access to this repository. Continue? From 641d37adc76f54803457473975726825ab12f549 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:24:04 +0800 Subject: [PATCH 29/80] fix: use web.Bind --- routers/web/web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/web.go b/routers/web/web.go index 4031df8b138d1..dda65fb848d9a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -776,7 +776,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/secrets", func() { m.Get("", org.Secrets) - m.Post("", bindIgnErr(forms.AddSecretForm{}), org.SecretsPost) + m.Post("", web.Bind(forms.AddSecretForm{}), org.SecretsPost) m.Post("/delete", org.SecretsDelete) }) From 7c82f7abac347a4b1b7b35e3747ea76f86463624 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:25:34 +0800 Subject: [PATCH 30/80] fix: remove PullRequestRead --- services/forms/user_form.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 6079dacde2c40..4d9107df84fc1 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -350,14 +350,13 @@ func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding // AddKeyForm form for adding SSH/GPG key type AddKeyForm struct { - Type string `binding:"OmitEmpty"` - Title string `binding:"Required;MaxSize(50)"` - Content string `binding:"Required"` - Signature string `binding:"OmitEmpty"` - KeyID string `binding:"OmitEmpty"` - Fingerprint string `binding:"OmitEmpty"` - IsWritable bool - PullRequestRead bool + Type string `binding:"OmitEmpty"` + Title string `binding:"Required;MaxSize(50)"` + Content string `binding:"Required"` + Signature string `binding:"OmitEmpty"` + KeyID string `binding:"OmitEmpty"` + Fingerprint string `binding:"OmitEmpty"` + IsWritable bool } // Validate validates the fields From f9d58d45b4144dac7da1b9f1333aeef9cb3a870f Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 16:59:20 +0800 Subject: [PATCH 31/80] fix: rename to secret_name --- templates/org/settings/secrets.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index b60cd4b2d5c35..b4cdbc0343cf4 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -20,7 +20,7 @@ {{.locale.Tr "repo.settings.secret_desc"}}
- +
diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index ca33f1e2b64b5..f040049a876e9 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -13,7 +13,7 @@ {{.locale.Tr "repo.settings.secret_desc"}}
- +
From aa10928182a18633aa05225ddab59b42e643d7de Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:07:43 +0800 Subject: [PATCH 32/80] Update templates/org/settings/secrets.tmpl Co-authored-by: delvh --- templates/org/settings/secrets.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index b4cdbc0343cf4..1756c54baf5e9 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -13,7 +13,7 @@
-
+
{{.CsrfTokenHtml}}
From f7380694e63f6d5c52c6c3568f1f8ae5a236624a Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:09:31 +0800 Subject: [PATCH 33/80] Update routers/web/org/setting.go Co-authored-by: delvh --- routers/web/org/setting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 751f04e661e13..011fcf8c65fbc 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -271,7 +271,7 @@ func Secrets(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) if err := secret_service.InsertOrgSecret(ctx, ctx.Org.Organization.ID, form.Title, form.Content); err != nil { - ctx.ServerError("InsertRepoSecret", err) + ctx.ServerError("InsertOrgSecret", err) return } From 5103f1de55622ec6862b04034fb888ba4fcfeccb Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:10:10 +0800 Subject: [PATCH 34/80] Update routers/web/org/setting.go Co-authored-by: delvh --- routers/web/org/setting.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 011fcf8c65fbc..6a1b8c2a91504 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -275,7 +275,7 @@ func SecretsPost(ctx *context.Context) { return } - log.Trace("Secret added: %d", ctx.Org.Organization.ID) + log.Trace("Org %d: secret added", ctx.Org.Organization.ID) ctx.Flash.Success(ctx.Tr("repo.settings.add_secret_success", form.Title)) ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") } From a23241f2e63b4b7ed4d35375f622db7c1bb42677 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:14:08 +0800 Subject: [PATCH 35/80] Update services/secrets/encryption_aes.go Co-authored-by: delvh --- services/secrets/encryption_aes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/secrets/encryption_aes.go b/services/secrets/encryption_aes.go index 790fe3f32027a..d8785ecb95a10 100644 --- a/services/secrets/encryption_aes.go +++ b/services/secrets/encryption_aes.go @@ -58,7 +58,7 @@ func (e *aesEncryptionProvider) Decrypt(enc, key []byte) ([]byte, error) { } if len(enc) < c.NonceSize() { - return nil, fmt.Errorf("encrypted value too short") + return nil, fmt.Errorf("encrypted value has length %d, which is too short for expected %d", len(enc), c.NonceSize()) } nonce := enc[:c.NonceSize()] From 9f8fdaac082a95dda8a33f6de5c196ca0083b68d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:15:37 +0800 Subject: [PATCH 36/80] Update templates/install.tmpl Co-authored-by: delvh --- templates/install.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/install.tmpl b/templates/install.tmpl index e2fd87aadfaa2..e8cbdccc659ef 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -170,7 +170,7 @@ {{.locale.Tr "install.enable_update_checker_helper"}}
- +

{{.locale.Tr "install.security_title"}}

From b32bb7a38954429dbf4dcf31087fc8a7481faed4 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 13 Dec 2022 17:16:26 +0800 Subject: [PATCH 37/80] Update templates/repo/settings/secrets.tmpl Co-authored-by: delvh --- templates/repo/settings/secrets.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index f040049a876e9..736a2aabcebd1 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -6,7 +6,7 @@
-
+
{{.CsrfTokenHtml}}
From a8c192daa4152e373b3ee5345a2b69064378a20d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 15:18:38 +0800 Subject: [PATCH 38/80] fix: rename to owner --- models/auth/secret.go | 2 +- models/migrations/v1_19/v236.go | 2 +- routers/web/org/setting.go | 8 ++++---- services/secrets/secrets.go | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index c7c2cbb8bcdba..74a724dc2007a 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -36,7 +36,7 @@ var nameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9-_.]*$") // Secret represents a secret type Secret struct { ID int64 - UserID int64 `xorm:"index NOTNULL"` + OwnerID int64 `xorm:"index NOTNULL"` RepoID int64 `xorm:"index NOTNULL"` Name string `xorm:"NOTNULL"` Data string `xorm:"LONGTEXT"` // encrypted data, or plaintext data if there's no master key diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index e2f698c57f483..7d071a12eb2c2 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -12,7 +12,7 @@ import ( func CreateSecretsTable(x *xorm.Engine) error { type Secret struct { ID int64 - UserID int64 `xorm:"index NOTNULL"` + OwnerID int64 `xorm:"index NOTNULL"` RepoID int64 `xorm:"index NOTNULL"` Name string `xorm:"NOTNULL"` Data string `xorm:"LONGTEXT"` diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 6a1b8c2a91504..7704c18ad2007 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -257,9 +257,9 @@ func Secrets(ctx *context.Context) { ctx.Data["PageIsOrgSettingsSecrets"] = true ctx.Data["RequireTribute"] = true - secrets, err := secret_service.FindUserSecrets(ctx, ctx.Org.Organization.ID) + secrets, err := secret_service.FindOwnerSecrets(ctx, ctx.Org.Organization.ID) if err != nil { - ctx.ServerError("FindUserSecrets", err) + ctx.ServerError("FindOwnerSecrets", err) return } ctx.Data["Secrets"] = secrets @@ -270,8 +270,8 @@ func Secrets(ctx *context.Context) { // SecretsPost add secrets func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - if err := secret_service.InsertOrgSecret(ctx, ctx.Org.Organization.ID, form.Title, form.Content); err != nil { - ctx.ServerError("InsertOrgSecret", err) + if err := secret_service.InsertOwnerSecret(ctx, ctx.Org.Organization.ID, form.Title, form.Content); err != nil { + ctx.ServerError("InsertOwnerSecret", err) return } diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 628079e696bc3..224785b6b2eb2 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -117,15 +117,15 @@ func InsertRepoSecret(ctx context.Context, repoID int64, key, data string) error }) } -func InsertOrgSecret(ctx context.Context, userID int64, key, data string) error { +func InsertOwnerSecret(ctx context.Context, ownerID int64, key, data string) error { v, err := EncryptString(data) if err != nil { return err } return db.Insert(ctx, &auth_model.Secret{ - UserID: userID, - Name: key, - Data: v, + OwnerID: ownerID, + Name: key, + Data: v, }) } @@ -139,7 +139,7 @@ func FindRepoSecrets(ctx context.Context, repoID int64) ([]*auth_model.Secret, e return res, db.FindObjects(ctx, builder.Eq{"repo_id": repoID}, nil, &res) } -func FindUserSecrets(ctx context.Context, userID int64) ([]*auth_model.Secret, error) { +func FindOwnerSecrets(ctx context.Context, ownerID int64) ([]*auth_model.Secret, error) { var res []*auth_model.Secret - return res, db.FindObjects(ctx, builder.Eq{"user_id": userID}, nil, &res) + return res, db.FindObjects(ctx, builder.Eq{"owner_id": ownerID}, nil, &res) } From f1ef5ae58d0bb5913b95fef814acc1f4da414054 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 15:21:17 +0800 Subject: [PATCH 39/80] fix: remove FindObjects --- models/db/search.go | 18 ------------------ services/secrets/secrets.go | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/models/db/search.go b/models/db/search.go index ebb8eef227077..f5273cb6f6bfa 100644 --- a/models/db/search.go +++ b/models/db/search.go @@ -3,12 +3,6 @@ package db -import ( - "context" - - "xorm.io/builder" -) - // SearchOrderBy is used to sort the result type SearchOrderBy string @@ -33,15 +27,3 @@ const ( SearchOrderByForks SearchOrderBy = "num_forks ASC" SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" ) - -// FindObjects represents a common function to find Objects from database according cond and ListOptions -func FindObjects[Object any](ctx context.Context, cond builder.Cond, opts *ListOptions, objects *[]*Object) error { - sess := GetEngine(ctx).Where(cond) - if opts != nil && opts.PageSize > 0 { - if opts.Page < 1 { - opts.Page = 1 - } - sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1)) - } - return sess.Find(objects) -} diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 224785b6b2eb2..1fb9cc140aab0 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -136,10 +136,10 @@ func DeleteSecretByID(ctx context.Context, id int64) error { func FindRepoSecrets(ctx context.Context, repoID int64) ([]*auth_model.Secret, error) { var res []*auth_model.Secret - return res, db.FindObjects(ctx, builder.Eq{"repo_id": repoID}, nil, &res) + return res, db.GetEngine(ctx).Where(builder.Eq{"repo_id": repoID}).Find(&res) } func FindOwnerSecrets(ctx context.Context, ownerID int64) ([]*auth_model.Secret, error) { var res []*auth_model.Secret - return res, db.FindObjects(ctx, builder.Eq{"owner_id": ownerID}, nil, &res) + return res, db.GetEngine(ctx).Where(builder.Eq{"owner_id": ownerID}).Find(&res) } From 4a2676e457a8beff97cf17ab4666be78e7880715 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 15:42:52 +0800 Subject: [PATCH 40/80] fix: add unique index --- models/auth/secret.go | 6 +++--- models/migrations/v1_19/v236.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index 74a724dc2007a..dfbe1a8a15885 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -36,9 +36,9 @@ var nameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9-_.]*$") // Secret represents a secret type Secret struct { ID int64 - OwnerID int64 `xorm:"index NOTNULL"` - RepoID int64 `xorm:"index NOTNULL"` - Name string `xorm:"NOTNULL"` + OwnerID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + RepoID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + Name string `xorm:"UNIQUE(owner_repo_name) NOTNULL"` Data string `xorm:"LONGTEXT"` // encrypted data, or plaintext data if there's no master key CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"` } diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index 7d071a12eb2c2..330df0bc8994b 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -12,9 +12,9 @@ import ( func CreateSecretsTable(x *xorm.Engine) error { type Secret struct { ID int64 - OwnerID int64 `xorm:"index NOTNULL"` - RepoID int64 `xorm:"index NOTNULL"` - Name string `xorm:"NOTNULL"` + OwnerID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + RepoID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + Name string `xorm:"UNIQUE(owner_repo_name) NOTNULL"` Data string `xorm:"LONGTEXT"` CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"` } From 5aa55fe2de6b4f8d0b7cd44220c97de8c3c038d3 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 15:49:06 +0800 Subject: [PATCH 41/80] fix: delete secrets --- services/org/org.go | 5 +++++ services/repository/repository.go | 5 +++++ services/secrets/secrets.go | 10 ++++++++++ 3 files changed, 20 insertions(+) diff --git a/services/org/org.go b/services/org/org.go index e45fb305debe8..06c7ced5a1ba3 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -14,6 +14,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" + secret_service "code.gitea.io/gitea/services/secrets" ) // DeleteOrganization completely and permanently deletes everything of organization. @@ -39,6 +40,10 @@ func DeleteOrganization(org *organization.Organization) error { return models.ErrUserOwnPackages{UID: org.ID} } + if err := secret_service.DeleteSecretsByOwnerID(ctx, org.ID); err != nil { + return err + } + if err := organization.DeleteOrganization(ctx, org); err != nil { return fmt.Errorf("DeleteOrganization: %w", err) } diff --git a/services/repository/repository.go b/services/repository/repository.go index 3c3e7e82c3f8f..bf4c5fb35b2c1 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -21,6 +21,7 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" pull_service "code.gitea.io/gitea/services/pull" + secret_service "code.gitea.io/gitea/services/secrets" ) // CreateRepository creates a repository for the user/organization. @@ -51,6 +52,10 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod return err } + if err := secret_service.DeleteSecretsByRepoID(ctx, repo.ID); err != nil { + return err + } + return packages_model.UnlinkRepositoryFromAllPackages(ctx, repo.ID) } diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 1fb9cc140aab0..4d304e2aca42f 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -134,6 +134,16 @@ func DeleteSecretByID(ctx context.Context, id int64) error { return err } +func DeleteSecretsByRepoID(ctx context.Context, repoID int64) error { + _, err := db.DeleteByBean(ctx, &auth_model.Secret{RepoID: repoID}) + return err +} + +func DeleteSecretsByOwnerID(ctx context.Context, ownerID int64) error { + _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: ownerID}) + return err +} + func FindRepoSecrets(ctx context.Context, repoID int64) ([]*auth_model.Secret, error) { var res []*auth_model.Secret return res, db.GetEngine(ctx).Where(builder.Eq{"repo_id": repoID}).Find(&res) From d1a729b414c8f9b01ffdec16cb69cb112c41100c Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 15:59:57 +0800 Subject: [PATCH 42/80] fix: generate master key with 64 chars --- modules/generate/generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 2cb92e9b74b71..2b7423b9d9542 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -69,5 +69,5 @@ func NewSecretKey() (string, error) { // NewMasterKey generate a new value intended to be used by MASTER_KEY. func NewMasterKey() ([]byte, error) { - return util.CryptoRandomBytes(32) + return util.CryptoRandomBytes(64) } From 00f305ff1e94b3ec8b6f4e33026348eb0f7f5f2f Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 17:52:52 +0800 Subject: [PATCH 43/80] chore: revert files about master key --- cmd/generate.go | 54 --------- modules/generate/generate.go | 5 - modules/setting/setting.go | 17 --- routers/init.go | 3 - routers/install/install.go | 34 ------ services/secrets/encryption.go | 15 --- services/secrets/encryption_aes.go | 87 ------------- services/secrets/encryption_aes_test.go | 21 ---- services/secrets/masterkey.go | 26 ---- services/secrets/masterkey_nop.go | 41 ------- services/secrets/masterkey_nop_test.go | 15 --- services/secrets/masterkey_plain.go | 58 --------- services/secrets/secrets.go | 155 ------------------------ templates/install.tmpl | 16 --- 14 files changed, 547 deletions(-) delete mode 100644 services/secrets/encryption.go delete mode 100644 services/secrets/encryption_aes.go delete mode 100644 services/secrets/encryption_aes_test.go delete mode 100644 services/secrets/masterkey.go delete mode 100644 services/secrets/masterkey_nop.go delete mode 100644 services/secrets/masterkey_nop_test.go delete mode 100644 services/secrets/masterkey_plain.go delete mode 100644 services/secrets/secrets.go diff --git a/cmd/generate.go b/cmd/generate.go index 44dfce5700636..f72ee16390486 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -5,14 +5,10 @@ package cmd import ( - "encoding/base64" "fmt" "os" "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/services/secrets" "github.com/mattn/go-isatty" "github.com/urfave/cli" @@ -35,7 +31,6 @@ var ( microcmdGenerateInternalToken, microcmdGenerateLfsJwtSecret, microcmdGenerateSecretKey, - microcmdGenerateMasterKey, }, } @@ -57,12 +52,6 @@ var ( Usage: "Generate a new SECRET_KEY", Action: runGenerateSecretKey, } - - microcmdGenerateMasterKey = cli.Command{ - Name: "MASTER_KEY", - Usage: "Generate a new MASTER_KEY", - Action: runGenerateMasterKey, - } ) func runGenerateInternalToken(c *cli.Context) error { @@ -109,46 +98,3 @@ func runGenerateSecretKey(c *cli.Context) error { return nil } - -func runGenerateMasterKey(c *cli.Context) error { - // Silence the console logger - log.DelNamedLogger("console") - log.DelNamedLogger(log.DEFAULT) - - // Read configuration file - setting.LoadFromExisting() - - providerType := secrets.MasterKeyProviderType(setting.MasterKeyProvider) - if providerType == secrets.MasterKeyProviderTypeNone { - return fmt.Errorf("configured master key provider does not support key generation") - } - - if err := secrets.Init(); err != nil { - return err - } - - scrts, err := secrets.GenerateMasterKey() - if err != nil { - return err - } - - if len(scrts) > 1 { - fmt.Println("Unseal secrets:") - for i, secret := range scrts { - if i > 0 { - fmt.Printf("\n") - } - fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(secret)) - } - } - - if providerType == secrets.MasterKeyProviderTypePlain && len(scrts) == 1 { - fmt.Printf("%s", base64.StdEncoding.EncodeToString(scrts[0])) - - if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") - } - } - - return nil -} diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 2b7423b9d9542..f29634e05e663 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -66,8 +66,3 @@ func NewSecretKey() (string, error) { return secretKey, nil } - -// NewMasterKey generate a new value intended to be used by MASTER_KEY. -func NewMasterKey() ([]byte, error) { - return util.CryptoRandomBytes(64) -} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index f678fe0e35f98..47e0ae2cda1f5 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -5,7 +5,6 @@ package setting import ( - "crypto/sha1" "encoding/base64" "fmt" "math" @@ -28,7 +27,6 @@ import ( "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" - "golang.org/x/crypto/pbkdf2" gossh "golang.org/x/crypto/ssh" ini "gopkg.in/ini.v1" ) @@ -216,8 +214,6 @@ var ( HMACKey string `ini:"HMAC_KEY"` Allways bool }{} - MasterKeyProvider string - MasterKey []byte // UI settings UI = struct { @@ -977,19 +973,6 @@ func loadFromConf(allowEmpty bool, extraConfig string) { PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false) SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20) - // Master key provider configuration - MasterKeyProvider = sec.Key("MASTER_KEY_PROVIDER").MustString("plain") - switch MasterKeyProvider { - case "plain": - tempSalt := []byte{'g', 'i', 't', 'e', 'a'} - MasterKey = []byte(sec.Key("MASTER_KEY").MustString(SecretKey)) - MasterKey = pbkdf2.Key(MasterKey, tempSalt, 4096, 32, sha1.New) - case "none": - default: - log.Fatal("invalid master key provider type: %v", MasterKeyProvider) - return - } - InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN") if InstallLock && InternalToken == "" { // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate diff --git a/routers/init.go b/routers/init.go index 4345b60291705..670191debc664 100644 --- a/routers/init.go +++ b/routers/init.go @@ -46,7 +46,6 @@ import ( pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/repository/archiver" - secret_service "code.gitea.io/gitea/services/secrets" "code.gitea.io/gitea/services/task" "code.gitea.io/gitea/services/webhook" ) @@ -150,8 +149,6 @@ func GlobalInitInstalled(ctx context.Context) { mustInit(models.Init) mustInit(repo_service.Init) - mustInit(secret_service.Init) - // Booting long running goroutines. issue_indexer.InitIssueIndexer(false) code_indexer.Init() diff --git a/routers/install/install.go b/routers/install/install.go index 96785ad3af0f3..ab37f9ba35fed 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -6,7 +6,6 @@ package install import ( goctx "context" - "encoding/base64" "fmt" "net/http" "os" @@ -34,7 +33,6 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/forms" - "code.gitea.io/gitea/services/secrets" "gitea.com/go-chi/session" "gopkg.in/ini.v1" @@ -164,7 +162,6 @@ func Install(ctx *context.Context) { form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress form.PasswordAlgorithm = setting.PasswordHashAlgo - form.MasterKeyProvider = secrets.MasterKeyProviderTypePlain middleware.AssignForm(form, ctx.Data) ctx.HTML(http.StatusOK, tplInstall) @@ -390,40 +387,10 @@ func SubmitInstall(ctx *context.Context) { log.Error("Failed to load custom conf '%s': %v", setting.CustomConf, err) } } - - // Setup master key provider - cfg.Section("security").Key("MASTER_KEY_PROVIDER").SetValue(string(form.MasterKeyProvider)) - var provider secrets.MasterKeyProvider - switch form.MasterKeyProvider { - case secrets.MasterKeyProviderTypePlain: - provider = secrets.NewPlainMasterKeyProvider() - } - var masterKey []byte - if provider != nil { - if err = provider.Init(); err != nil { - ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) - return - } - // Generate master key - if _, err = provider.GenerateMasterKey(); err != nil { - ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) - return - } - masterKey, err = provider.GetMasterKey() - if err != nil { - ctx.RenderWithErr(ctx.Tr("install.master_key_failed", err), tplInstall, &form) - return - } - if form.MasterKeyProvider == secrets.MasterKeyProviderTypePlain { - cfg.Section("security").Key("MASTER_KEY").SetValue(base64.StdEncoding.EncodeToString(masterKey)) - } - } - cfg.Section("database").Key("DB_TYPE").SetValue(setting.Database.Type) cfg.Section("database").Key("HOST").SetValue(setting.Database.Host) cfg.Section("database").Key("NAME").SetValue(setting.Database.Name) cfg.Section("database").Key("USER").SetValue(setting.Database.User) - // TODO: Encrypt secret cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd) cfg.Section("database").Key("SCHEMA").SetValue(setting.Database.Schema) cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode) @@ -465,7 +432,6 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom) cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser) - // TODO: Encrypt secret cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) } else { cfg.Section("mailer").Key("ENABLED").SetValue("false") diff --git a/services/secrets/encryption.go b/services/secrets/encryption.go deleted file mode 100644 index 2f07d05df9e4d..0000000000000 --- a/services/secrets/encryption.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -// EncryptionProvider encrypts and decrypts secrets -type EncryptionProvider interface { - Encrypt(secret, key []byte) ([]byte, error) - - EncryptString(secret string, key []byte) (string, error) - - Decrypt(enc, key []byte) ([]byte, error) - - DecryptString(enc string, key []byte) (string, error) -} diff --git a/services/secrets/encryption_aes.go b/services/secrets/encryption_aes.go deleted file mode 100644 index d8785ecb95a10..0000000000000 --- a/services/secrets/encryption_aes.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "fmt" - "io" -) - -type aesEncryptionProvider struct{} - -func NewAesEncryptionProvider() EncryptionProvider { - return &aesEncryptionProvider{} -} - -func (e *aesEncryptionProvider) Encrypt(secret, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - c, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - nonce := make([]byte, c.NonceSize(), c.NonceSize()+c.Overhead()+len(secret)) - if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return nil, err - } - out := c.Seal(nil, nonce, secret, nil) - - return append(nonce, out...), nil -} - -func (e *aesEncryptionProvider) EncryptString(secret string, key []byte) (string, error) { - out, err := e.Encrypt([]byte(secret), key) - if err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(out), nil -} - -func (e *aesEncryptionProvider) Decrypt(enc, key []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - c, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(enc) < c.NonceSize() { - return nil, fmt.Errorf("encrypted value has length %d, which is too short for expected %d", len(enc), c.NonceSize()) - } - - nonce := enc[:c.NonceSize()] - ciphertext := enc[c.NonceSize():] - - out, err := c.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return out, nil -} - -func (e *aesEncryptionProvider) DecryptString(enc string, key []byte) (string, error) { - encb, err := base64.StdEncoding.DecodeString(enc) - if err != nil { - return "", err - } - - out, err := e.Decrypt(encb, key) - if err != nil { - return "", err - } - - return string(out), nil -} diff --git a/services/secrets/encryption_aes_test.go b/services/secrets/encryption_aes_test.go deleted file mode 100644 index 18e0fd10697f1..0000000000000 --- a/services/secrets/encryption_aes_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEncryptDecrypt(t *testing.T) { - provider := NewAesEncryptionProvider() - key := []byte("1111111111111111") - pri := "vvvvvvv" - enc, err := provider.EncryptString(pri, key) - assert.NoError(t, err) - v, err := provider.DecryptString(enc, key) - assert.NoError(t, err) - assert.EqualValues(t, pri, v) -} diff --git a/services/secrets/masterkey.go b/services/secrets/masterkey.go deleted file mode 100644 index 2ad18954d8f81..0000000000000 --- a/services/secrets/masterkey.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "fmt" -) - -// ErrMasterKeySealed is returned when trying to use master key that is sealed -var ErrMasterKeySealed = fmt.Errorf("master key sealed") - -// MasterKeyProvider provides master key used for encryption -type MasterKeyProvider interface { - Init() error - - GenerateMasterKey() ([][]byte, error) - - Unseal(secret []byte) error - - Seal() error - - IsSealed() bool - - GetMasterKey() ([]byte, error) -} diff --git a/services/secrets/masterkey_nop.go b/services/secrets/masterkey_nop.go deleted file mode 100644 index dc9b93a7ca432..0000000000000 --- a/services/secrets/masterkey_nop.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -type nopMasterKeyProvider struct{} - -// NewNopMasterKeyProvider returns master key provider that holds no master key and is always unsealed -func NewNopMasterKeyProvider() MasterKeyProvider { - return &nopMasterKeyProvider{} -} - -// Init initializes master key provider -func (k *nopMasterKeyProvider) Init() error { - return nil -} - -// GenerateMasterKey always returns empty master key -func (k *nopMasterKeyProvider) GenerateMasterKey() ([][]byte, error) { - return nil, nil -} - -// Unseal master key by providing unsealing secret -func (k *nopMasterKeyProvider) Unseal(secret []byte) error { - return nil -} - -// Seal master key -func (k *nopMasterKeyProvider) Seal() error { - return nil -} - -// IsSealed always returns false -func (k *nopMasterKeyProvider) IsSealed() bool { - return false -} - -// GetMasterKey returns empty master key -func (k *nopMasterKeyProvider) GetMasterKey() ([]byte, error) { - return nil, nil -} diff --git a/services/secrets/masterkey_nop_test.go b/services/secrets/masterkey_nop_test.go deleted file mode 100644 index 330ecf9919903..0000000000000 --- a/services/secrets/masterkey_nop_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNopMasterKey_IsSealed(t *testing.T) { - k := NewNopMasterKeyProvider() - assert.False(t, k.IsSealed()) -} diff --git a/services/secrets/masterkey_plain.go b/services/secrets/masterkey_plain.go deleted file mode 100644 index d299bdbf63826..0000000000000 --- a/services/secrets/masterkey_plain.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/setting" -) - -type plainMasterKeyProvider struct { - key []byte -} - -// NewPlainMasterKeyProvider returns unsecured static master key provider -func NewPlainMasterKeyProvider() MasterKeyProvider { - return &plainMasterKeyProvider{} -} - -// Init initializes master key provider -func (k *plainMasterKeyProvider) Init() error { - return k.Unseal(nil) -} - -// GenerateMasterKey generates a new master key and returns secret or secrets for unsealing -func (k *plainMasterKeyProvider) GenerateMasterKey() ([][]byte, error) { - key, err := generate.NewMasterKey() - if err != nil { - return nil, err - } - k.key = key - return [][]byte{key}, nil -} - -// Unseal master key by providing unsealing secret -func (k *plainMasterKeyProvider) Unseal(secret []byte) error { - k.key = setting.MasterKey - return nil -} - -// Seal master key -func (k *plainMasterKeyProvider) Seal() error { - k.key = nil - return nil -} - -// IsSealed returns if master key is sealed -func (k *plainMasterKeyProvider) IsSealed() bool { - return len(k.key) == 0 -} - -// GetMasterKey returns master key -func (k *plainMasterKeyProvider) GetMasterKey() ([]byte, error) { - if k.IsSealed() { - return nil, ErrMasterKeySealed - } - return k.key, nil -} diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go deleted file mode 100644 index 4d304e2aca42f..0000000000000 --- a/services/secrets/secrets.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package secrets - -import ( - "context" - "fmt" - - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" - - "xorm.io/builder" -) - -// MasterKeyProviderType is the type of master key provider -type MasterKeyProviderType string - -// Types of master key providers -const ( - MasterKeyProviderTypeNone MasterKeyProviderType = "none" - MasterKeyProviderTypePlain MasterKeyProviderType = "plain" -) - -var ( - masterKey MasterKeyProvider - encProvider EncryptionProvider -) - -// Init initializes master key provider based on settings -func Init() error { - switch MasterKeyProviderType(setting.MasterKeyProvider) { - case MasterKeyProviderTypeNone: - masterKey = NewNopMasterKeyProvider() - case MasterKeyProviderTypePlain: - masterKey = NewPlainMasterKeyProvider() - default: - return fmt.Errorf("invalid master key provider %v", setting.MasterKeyProvider) - } - - if err := masterKey.Init(); err != nil { - return err - } - - encProvider = NewAesEncryptionProvider() - - return nil -} - -// GenerateMasterKey generates a new master key and returns secret or secrets for unsealing -func GenerateMasterKey() ([][]byte, error) { - return masterKey.GenerateMasterKey() -} - -func Encrypt(secret []byte) ([]byte, error) { - key, err := masterKey.GetMasterKey() - if err != nil { - return nil, err - } - - if len(key) == 0 { - return secret, nil - } - - return encProvider.Encrypt(secret, key) -} - -func EncryptString(secret string) (string, error) { - key, err := masterKey.GetMasterKey() - if err != nil { - return "", err - } - - if len(key) == 0 { - return secret, nil - } - - return encProvider.EncryptString(secret, key) -} - -func Decrypt(enc []byte) ([]byte, error) { - key, err := masterKey.GetMasterKey() - if err != nil { - return nil, err - } - - if len(key) == 0 { - return enc, nil - } - - return encProvider.Decrypt(enc, key) -} - -func DecryptString(enc string) (string, error) { - key, err := masterKey.GetMasterKey() - if err != nil { - return "", err - } - - if len(key) == 0 { - return enc, nil - } - - return encProvider.DecryptString(enc, key) -} - -func InsertRepoSecret(ctx context.Context, repoID int64, key, data string) error { - v, err := EncryptString(data) - if err != nil { - return err - } - return db.Insert(ctx, &auth_model.Secret{ - RepoID: repoID, - Name: key, - Data: v, - }) -} - -func InsertOwnerSecret(ctx context.Context, ownerID int64, key, data string) error { - v, err := EncryptString(data) - if err != nil { - return err - } - return db.Insert(ctx, &auth_model.Secret{ - OwnerID: ownerID, - Name: key, - Data: v, - }) -} - -func DeleteSecretByID(ctx context.Context, id int64) error { - _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}) - return err -} - -func DeleteSecretsByRepoID(ctx context.Context, repoID int64) error { - _, err := db.DeleteByBean(ctx, &auth_model.Secret{RepoID: repoID}) - return err -} - -func DeleteSecretsByOwnerID(ctx context.Context, ownerID int64) error { - _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: ownerID}) - return err -} - -func FindRepoSecrets(ctx context.Context, repoID int64) ([]*auth_model.Secret, error) { - var res []*auth_model.Secret - return res, db.GetEngine(ctx).Where(builder.Eq{"repo_id": repoID}).Find(&res) -} - -func FindOwnerSecrets(ctx context.Context, ownerID int64) ([]*auth_model.Secret, error) { - var res []*auth_model.Secret - return res, db.GetEngine(ctx).Where(builder.Eq{"owner_id": ownerID}).Find(&res) -} diff --git a/templates/install.tmpl b/templates/install.tmpl index e8cbdccc659ef..0625f43cc4e62 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -170,22 +170,6 @@ {{.locale.Tr "install.enable_update_checker_helper"}}
- -

{{.locale.Tr "install.security_title"}}

- -
- - - {{.locale.Tr "install.master_key_provider_helper"}} -

{{.locale.Tr "install.optional_title"}}

From b1a1926f96ea46984701d98530d1e7af17678c7d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 14 Dec 2022 19:59:22 +0800 Subject: [PATCH 44/80] fix: make it compile --- models/auth/secret.go | 32 +++++++++++++++++++++++++++++++ modules/templates/helper.go | 4 ++-- routers/web/org/setting.go | 27 +++++++++++++++++++------- routers/web/repo/setting.go | 27 +++++++++++++++++++------- services/forms/user_form.go | 2 -- services/org/org.go | 4 ++-- services/repository/repository.go | 4 ++-- 7 files changed, 78 insertions(+), 22 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index dfbe1a8a15885..60a650dbf7812 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -4,12 +4,15 @@ package auth import ( + "context" "fmt" "regexp" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) type ErrSecretInvalidValue struct { @@ -60,3 +63,32 @@ func (s *Secret) Validate() error { return nil } } + +type FindSecretsOptions struct { + db.ListOptions + OwnerID int64 + RepoID int64 +} + +func (opts *FindSecretsOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) + } + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + + return cond +} + +func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error) { + var secrets []*Secret + sess := db.GetEngine(ctx) + if opts.PageSize != 0 { + sess = db.SetSessionPagination(sess, &opts.ListOptions) + } + return secrets, sess. + Where(opts.toConds()). + Find(&secrets) +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index b4f46d4c6d45e..052544f4dabea 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -41,12 +41,12 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" - secret_service "code.gitea.io/gitea/services/secrets" "github.com/editorconfig/editorconfig-core-go/v2" ) @@ -464,7 +464,7 @@ func NewFuncMap() []template.FuncMap { return "******" }, "DecryptSecret": func(s string) string { - v, _ := secret_service.DecryptString(s) + v, _ := secret.DecryptSecret(setting.SecretKey, s) return v }, "CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string { diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 7704c18ad2007..8475ad412fcd7 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -18,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" user_setting "code.gitea.io/gitea/routers/web/user/setting" @@ -25,7 +27,6 @@ import ( "code.gitea.io/gitea/services/org" container_service "code.gitea.io/gitea/services/packages/container" repo_service "code.gitea.io/gitea/services/repository" - secret_service "code.gitea.io/gitea/services/secrets" user_service "code.gitea.io/gitea/services/user" ) @@ -257,9 +258,9 @@ func Secrets(ctx *context.Context) { ctx.Data["PageIsOrgSettingsSecrets"] = true ctx.Data["RequireTribute"] = true - secrets, err := secret_service.FindOwnerSecrets(ctx, ctx.Org.Organization.ID) + secrets, err := auth_model.FindSecrets(ctx, auth_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID}) if err != nil { - ctx.ServerError("FindOwnerSecrets", err) + ctx.ServerError("FindSecrets", err) return } ctx.Data["Secrets"] = secrets @@ -270,8 +271,19 @@ func Secrets(ctx *context.Context) { // SecretsPost add secrets func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - if err := secret_service.InsertOwnerSecret(ctx, ctx.Org.Organization.ID, form.Title, form.Content); err != nil { - ctx.ServerError("InsertOwnerSecret", err) + + data, err := secret.EncryptSecret(setting.SecretKey, form.Content) + if err != nil { + ctx.ServerError("EncryptSecret", err) + return + } + + if err := db.Insert(ctx, &auth_model.Secret{ + OwnerID: ctx.Org.Organization.ID, + Name: form.Title, + Data: data, + }); err != nil { + ctx.ServerError("Insert Secret", err) return } @@ -282,9 +294,10 @@ func SecretsPost(ctx *context.Context) { // SecretsDelete delete secrets func SecretsDelete(ctx *context.Context) { - if err := secret_service.DeleteSecretByID(ctx, ctx.FormInt64("id")); err != nil { + id := ctx.FormInt64("id") + if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed")) - log.Error("delete secret %d: %v", ctx.FormInt64("id"), err) + log.Error("delete secret %d: %v", id, err) } else { ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success")) } diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index a42a063915fb5..c6405795ecfb2 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" @@ -30,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/log" mirror_module "code.gitea.io/gitea/modules/mirror" repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" @@ -44,7 +46,6 @@ import ( mirror_service "code.gitea.io/gitea/services/mirror" org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" - secret_service "code.gitea.io/gitea/services/secrets" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -1114,9 +1115,9 @@ func DeployKeys(ctx *context.Context) { } ctx.Data["Deploykeys"] = keys - secrets, err := secret_service.FindRepoSecrets(ctx, ctx.Repo.Repository.ID) + secrets, err := auth_model.FindSecrets(ctx, auth_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { - ctx.ServerError("FindRepoSecrets", err) + ctx.ServerError("FindSecrets", err) return } ctx.Data["Secrets"] = secrets @@ -1127,8 +1128,19 @@ func DeployKeys(ctx *context.Context) { // SecretsPost response for creating a new secret func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddKeyForm) - if err := secret_service.InsertRepoSecret(ctx, ctx.Repo.Repository.ID, form.Title, form.Content); err != nil { - ctx.ServerError("InsertRepoSecret", err) + + data, err := secret.EncryptSecret(setting.SecretKey, form.Content) + if err != nil { + ctx.ServerError("EncryptSecret", err) + return + } + + if err := db.Insert(ctx, &auth_model.Secret{ + OwnerID: ctx.Repo.Repository.ID, + Name: form.Title, + Data: data, + }); err != nil { + ctx.ServerError("Insert Secret", err) return } @@ -1205,9 +1217,10 @@ func DeployKeysPost(ctx *context.Context) { } func DeleteSecret(ctx *context.Context) { - if err := secret_service.DeleteSecretByID(ctx, ctx.FormInt64("id")); err != nil { + id := ctx.FormInt64("id") + if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed")) - log.Error("delete secret %d: %v", ctx.FormInt64("id"), err) + log.Error("delete secret %d: %v", id, err) } else { ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success")) } diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 4d9107df84fc1..bbea58310a406 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/services/secrets" "gitea.com/go-chi/binding" ) @@ -64,7 +63,6 @@ type InstallForm struct { NoReplyAddress string PasswordAlgorithm string - MasterKeyProvider secrets.MasterKeyProviderType `binding:"Required;In(none,plain)"` AdminName string `binding:"OmitEmpty;Username;MaxSize(30)" locale:"install.admin_name"` AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"` diff --git a/services/org/org.go b/services/org/org.go index 06c7ced5a1ba3..7bc98686e4988 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -7,6 +7,7 @@ import ( "fmt" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" @@ -14,7 +15,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" - secret_service "code.gitea.io/gitea/services/secrets" ) // DeleteOrganization completely and permanently deletes everything of organization. @@ -40,7 +40,7 @@ func DeleteOrganization(org *organization.Organization) error { return models.ErrUserOwnPackages{UID: org.ID} } - if err := secret_service.DeleteSecretsByOwnerID(ctx, org.ID); err != nil { + if _, err := db.DeleteByBean(ctx, &auth_model.Secret{OwnerID: org.ID}); err != nil { return err } diff --git a/services/repository/repository.go b/services/repository/repository.go index bf4c5fb35b2c1..0299e59819b5e 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -8,6 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" @@ -21,7 +22,6 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" pull_service "code.gitea.io/gitea/services/pull" - secret_service "code.gitea.io/gitea/services/secrets" ) // CreateRepository creates a repository for the user/organization. @@ -52,7 +52,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod return err } - if err := secret_service.DeleteSecretsByRepoID(ctx, repo.ID); err != nil { + if _, err := db.DeleteByBean(ctx, &auth_model.Secret{RepoID: repo.ID}); err != nil { return err } From 402c8aac6a815bac1f72f36f0523fae1af5aab3d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:08:38 +0800 Subject: [PATCH 45/80] chore: remove In --- modules/web/middleware/binding.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 4e025e6dfa2df..733f00a1d5662 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -78,11 +78,6 @@ func GetInclude(field reflect.StructField) string { return getRuleBody(field, "Include(") } -// GetIn get allowed values in form tag -func GetIn(field reflect.StructField) string { - return getRuleBody(field, "In(") -} - // Validate validate TODO: func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors { if errs.Len() == 0 { @@ -135,8 +130,6 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl data["ErrorMsg"] = trName + l.Tr("form.url_error", errs[0].Message) case binding.ERR_INCLUDE: data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) - case binding.ERR_IN: - data["ErrorMsg"] = trName + l.Tr("form.in_error", strings.Join(strings.Split(GetIn(field), ","), ", ")) case validation.ErrGlobPattern: data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) case validation.ErrRegexPattern: From caecad3348a43da10405b8b2be5ed10ef3d14a60 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:10:35 +0800 Subject: [PATCH 46/80] fix: delete secrets --- models/organization/org.go | 2 ++ models/repo.go | 2 ++ services/org/org.go | 5 ----- services/repository/repository.go | 5 ----- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index b3d77b4ec6967..1192ec7407686 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -370,6 +371,7 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { &TeamUser{OrgID: org.ID}, &TeamUnit{OrgID: org.ID}, &TeamInvite{OrgID: org.ID}, + &auth_model.Secret{OwnerID: org.ID}, ); err != nil { return fmt.Errorf("DeleteBeans: %w", err) } diff --git a/models/repo.go b/models/repo.go index 9af600c9ba4a7..d78a775e1cdc7 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,6 +14,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" admin_model "code.gitea.io/gitea/models/admin" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -150,6 +151,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &admin_model.Task{RepoID: repoID}, &repo_model.Watch{RepoID: repoID}, &webhook.Webhook{RepoID: repoID}, + &auth_model.Secret{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/services/org/org.go b/services/org/org.go index 7bc98686e4988..e45fb305debe8 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -7,7 +7,6 @@ import ( "fmt" "code.gitea.io/gitea/models" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" @@ -40,10 +39,6 @@ func DeleteOrganization(org *organization.Organization) error { return models.ErrUserOwnPackages{UID: org.ID} } - if _, err := db.DeleteByBean(ctx, &auth_model.Secret{OwnerID: org.ID}); err != nil { - return err - } - if err := organization.DeleteOrganization(ctx, org); err != nil { return fmt.Errorf("DeleteOrganization: %w", err) } diff --git a/services/repository/repository.go b/services/repository/repository.go index 0299e59819b5e..3c3e7e82c3f8f 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -8,7 +8,6 @@ import ( "fmt" "code.gitea.io/gitea/models" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" @@ -52,10 +51,6 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod return err } - if _, err := db.DeleteByBean(ctx, &auth_model.Secret{RepoID: repo.ID}); err != nil { - return err - } - return packages_model.UnlinkRepositoryFromAllPackages(ctx, repo.ID) } From 9139775ec50dcdccd9e4fcc02155dc84d880c373 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:13:26 +0800 Subject: [PATCH 47/80] chore: remove locale --- options/locale/locale_en-US.ini | 8 -------- 1 file changed, 8 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 105dee9cdddec..2b43f78bce1ba 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -185,12 +185,6 @@ app_url_helper = Base address for HTTP(S) clone URLs and email notifications. log_root_path = Log Path log_root_path_helper = Log files will be written to this directory. -security_title = Security Settings -master_key_provider = Master Key Provider -master_key_provider_none = None -master_key_provider_plain = Plain -master_key_provider_helper = Master Key Provider to use to store secret key that will be used for other secret encryption. Use "None" to not encrypt secrets. Use "Plain" to store automatically generated secret in configuration file. - optional_title = Optional Settings email_title = Email Settings smtp_addr = SMTP Host @@ -249,7 +243,6 @@ no_reply_address = Hidden Email Domain no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. password_algorithm = Password Hash Algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. `argon2` whilst having good characteristics uses a lot of memory and may be inappropriate for small systems. -master_key_failed = Failed to generate master key: %v enable_update_checker = Enable Update Checker enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. @@ -473,7 +466,6 @@ max_size_error = ` must contain at most %s characters.` email_error = ` is not a valid email address.` url_error = `'%s' is not a valid URL.` include_error = ` must contain substring '%s'.` -in_error = ` can only contain specific values: %s.` glob_pattern_error = ` glob pattern is invalid: %s.` regex_pattern_error = ` regex pattern is invalid: %s.` username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` From 91a30488ab301a77cb3787c54b86912343b8d782 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:17:50 +0800 Subject: [PATCH 48/80] chore: simplify template --- modules/templates/helper.go | 8 -------- templates/org/settings/secrets.tmpl | 4 +--- templates/repo/settings/secrets.tmpl | 4 +--- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 052544f4dabea..7b997b49d9e7d 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -41,7 +41,6 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/timeutil" @@ -460,13 +459,6 @@ func NewFuncMap() []template.FuncMap { return items }, "HasPrefix": strings.HasPrefix, - "Shadow": func(s string) string { - return "******" - }, - "DecryptSecret": func(s string) string { - v, _ := secret.DecryptSecret(setting.SecretKey, s) - return v - }, "CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string { var curBranch string if repo.ID != baseRepo.ID { diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 1756c54baf5e9..27c989854248d 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -49,9 +49,7 @@
{{.Name}} -
- {{Shadow .Data}} -
+
******
{{$.locale.Tr "settings.add_on"}} diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index 736a2aabcebd1..b2c5e8c08282f 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -42,9 +42,7 @@
{{.Name}} -
- {{Shadow .Data}} -
+
******
{{$.locale.Tr "settings.add_on"}} From 343c3b41c2a16c6b0110bb0cf54e70ad9944f5fd Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:25:04 +0800 Subject: [PATCH 49/80] fix: add index --- models/auth/secret.go | 4 ++-- models/migrations/v1_19/v236.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index 60a650dbf7812..b4f0e753909cf 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -39,8 +39,8 @@ var nameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9-_.]*$") // Secret represents a secret type Secret struct { ID int64 - OwnerID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` - RepoID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOTNULL"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOTNULL"` Name string `xorm:"UNIQUE(owner_repo_name) NOTNULL"` Data string `xorm:"LONGTEXT"` // encrypted data, or plaintext data if there's no master key CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"` diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index 330df0bc8994b..719b51d0fcc20 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -12,8 +12,8 @@ import ( func CreateSecretsTable(x *xorm.Engine) error { type Secret struct { ID int64 - OwnerID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` - RepoID int64 `xorm:"UNIQUE(owner_repo_name) NOTNULL"` + OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOTNULL"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOTNULL"` Name string `xorm:"UNIQUE(owner_repo_name) NOTNULL"` Data string `xorm:"LONGTEXT"` CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"` From b828e3bce97ba64ceb4ce0a439e9fbd01147f6ae Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:33:43 +0800 Subject: [PATCH 50/80] chore: remove RequireTribute --- routers/web/org/setting.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 8475ad412fcd7..5c79e6b012a1e 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -256,7 +256,6 @@ func Secrets(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.secrets") ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsOrgSettingsSecrets"] = true - ctx.Data["RequireTribute"] = true secrets, err := auth_model.FindSecrets(ctx, auth_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID}) if err != nil { From df8ad9217dc5ec397d30411a6158cf26eb4dbdae Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 11:53:26 +0800 Subject: [PATCH 51/80] fix: post repo secrets --- routers/web/repo/setting.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index c6405795ecfb2..39121c77300f7 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1136,9 +1136,9 @@ func SecretsPost(ctx *context.Context) { } if err := db.Insert(ctx, &auth_model.Secret{ - OwnerID: ctx.Repo.Repository.ID, - Name: form.Title, - Data: data, + RepoID: ctx.Repo.Repository.ID, + Name: form.Title, + Data: data, }); err != nil { ctx.ServerError("Insert Secret", err) return From ab588168e33a7e0e12252aa3a72da63e214425f7 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 12:16:03 +0800 Subject: [PATCH 52/80] feat: new locale --- options/locale/locale_en-US.ini | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2b43f78bce1ba..e9c7b805c95c3 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2059,18 +2059,6 @@ settings.deploy_key_desc = Deploy keys have read-only pull access to the reposit settings.is_writable = Enable Write Access settings.is_writable_info = Allow this deploy key to push to the repository. settings.no_deploy_keys = There are no deploy keys yet. -settings.secrets = Secrets -settings.add_secret = Add Secret -settings.add_secret_success = The secret '%s' has been added. -settings.secret_value_content_placeholder = Input any content -settings.secret_desc = Secrets will be passed to certain actions and cannot be read otherwise. -settings.secret_content = Value -settings.secret_name = Name -settings.no_secret = There are no secrets yet. -settings.secret_deletion = Remove secret -settings.secret_deletion_desc = Removing a secret will revoke its access to this repository. Continue? -settings.secret_deletion_success = The secret has been removed. -settings.secret_deletion_failed = Failed to remove secret. settings.title = Title settings.deploy_key_content = Content settings.key_been_used = A deploy key with identical content is already in use. @@ -2389,7 +2377,6 @@ settings.update_setting_success = Organization settings have been updated. settings.change_orgname_prompt = Note: changing the organization name also changes the organization's URL. settings.change_orgname_redirect_prompt = The old name will redirect until it is claimed. settings.update_avatar_success = The organization's avatar has been updated. -settings.secrets = Secrets settings.delete = Delete Organization settings.delete_account = Delete This Organization settings.delete_prompt = The organization will be permanently removed. This CANNOT be undone! @@ -3225,3 +3212,18 @@ owner.settings.cleanuprules.remove.days = Remove versions older than owner.settings.cleanuprules.remove.pattern = Remove versions matching owner.settings.cleanuprules.success.update = Cleanup rule has been updated. owner.settings.cleanuprules.success.delete = Cleanup rule has been deleted. + +[secrets] +secrets = Secrets +description = Secrets will be passed to certain actions and cannot be read otherwise. +none = There are no secrets yet. +value = Value +name = Name +creation = Add Secret +creation.success = The secret '%s' has been added. +creation.failed = Failed to remove secret. +creation.value_placeholder = Input any content +deletion = Remove secret +deletion.description = Removing a secret will revoke its access to repositories. Continue? +deletion.success = The secret has been removed. +deletion.failed = Failed to remove secret. \ No newline at end of file From 2b745d0695e005160690c27563d389180b8a381d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 12:36:02 +0800 Subject: [PATCH 53/80] fix: use new locale --- options/locale/locale_en-US.ini | 3 +-- routers/web/org/setting.go | 6 +++--- routers/web/repo/setting.go | 6 +++--- templates/org/settings/navbar.tmpl | 2 +- templates/org/settings/secrets.tmpl | 20 ++++++++++---------- templates/repo/settings/nav.tmpl | 2 +- templates/repo/settings/navbar.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 16 ++++++++-------- 8 files changed, 28 insertions(+), 29 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e9c7b805c95c3..3e76c929425e1 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3222,8 +3222,7 @@ name = Name creation = Add Secret creation.success = The secret '%s' has been added. creation.failed = Failed to remove secret. -creation.value_placeholder = Input any content deletion = Remove secret deletion.description = Removing a secret will revoke its access to repositories. Continue? deletion.success = The secret has been removed. -deletion.failed = Failed to remove secret. \ No newline at end of file +deletion.failed = Failed to remove secret. diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 5c79e6b012a1e..ca4cb592b1607 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -287,7 +287,7 @@ func SecretsPost(ctx *context.Context) { } log.Trace("Org %d: secret added", ctx.Org.Organization.ID) - ctx.Flash.Success(ctx.Tr("repo.settings.add_secret_success", form.Title)) + ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title)) ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") } @@ -295,10 +295,10 @@ func SecretsPost(ctx *context.Context) { func SecretsDelete(ctx *context.Context) { id := ctx.FormInt64("id") if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { - ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed")) + ctx.Flash.Error(ctx.Tr("secrets.deletion.failed")) log.Error("delete secret %d: %v", id, err) } else { - ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success")) + ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) } ctx.JSON(http.StatusOK, map[string]interface{}{ diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 39121c77300f7..1eb088b25ed45 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1145,7 +1145,7 @@ func SecretsPost(ctx *context.Context) { } log.Trace("Secret added: %d", ctx.Repo.Repository.ID) - ctx.Flash.Success(ctx.Tr("repo.settings.add_secret_success", form.Title)) + ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title)) ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") } @@ -1219,10 +1219,10 @@ func DeployKeysPost(ctx *context.Context) { func DeleteSecret(ctx *context.Context) { id := ctx.FormInt64("id") if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { - ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed")) + ctx.Flash.Error(ctx.Tr("secrets.deletion.failed")) log.Error("delete secret %d: %v", id, err) } else { - ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success")) + ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) } ctx.JSON(http.StatusOK, map[string]interface{}{ diff --git a/templates/org/settings/navbar.tmpl b/templates/org/settings/navbar.tmpl index 9ac45a855c731..765bb6aaaef29 100644 --- a/templates/org/settings/navbar.tmpl +++ b/templates/org/settings/navbar.tmpl @@ -13,7 +13,7 @@ {{.locale.Tr "repo.labels"}} - {{.locale.Tr "org.settings.secrets"}} + {{.locale.Tr "secrets.secrets"}} {{if .EnableOAuth2}} diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 27c989854248d..da28bfa92b583 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -7,9 +7,9 @@
{{template "base/alert" .}}

- {{.locale.Tr "repo.settings.secrets"}} + {{.locale.Tr "secrets.secrets"}}
-
{{.locale.Tr "repo.settings.add_secret"}}
+
{{.locale.Tr "secrets.creation"}}

@@ -17,18 +17,18 @@ {{.CsrfTokenHtml}}
- {{.locale.Tr "repo.settings.secret_desc"}} + {{.locale.Tr "secrets.description"}}
- +
- - + +
{{else}} - {{.locale.Tr "repo.settings.no_secret"}} + {{.locale.Tr "secrets.none"}} {{end}}
@@ -72,10 +72,10 @@ diff --git a/templates/repo/settings/nav.tmpl b/templates/repo/settings/nav.tmpl index 5cd1d86f78e41..3c00c5e188589 100644 --- a/templates/repo/settings/nav.tmpl +++ b/templates/repo/settings/nav.tmpl @@ -12,7 +12,7 @@ {{if or .SignedUser.AllowGitHook .SignedUser.IsAdmin}}
  • {{.locale.Tr "repo.settings.githooks"}}
  • {{end}} -
  • {{.locale.Tr "repo.settings.secrets"}}
  • +
  • {{.locale.Tr "secrets.secrets"}}
  • diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl index b1d6b04422ef8..236a82f34888a 100644 --- a/templates/repo/settings/navbar.tmpl +++ b/templates/repo/settings/navbar.tmpl @@ -25,7 +25,7 @@ {{end}} - {{.locale.Tr "repo.settings.secrets"}} + {{.locale.Tr "secrets.secrets"}} {{if .LFSStartServer}} diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index b2c5e8c08282f..f5f0a65174476 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -1,8 +1,8 @@

    - {{.locale.Tr "repo.settings.secrets"}} + {{.locale.Tr "secrets.secrets"}}
    -
    {{.locale.Tr "repo.settings.add_secret"}}
    +
    {{.locale.Tr "secrets.creation"}}

    @@ -10,18 +10,18 @@ {{.CsrfTokenHtml}}
    - {{.locale.Tr "repo.settings.secret_desc"}} + {{.locale.Tr "secrets.description"}}
    - +
    - - + +
    {{else}} - {{.locale.Tr "repo.settings.no_secret"}} + {{.locale.Tr "secrets.none"}} {{end}}
    From 674fced50f90f0c84f071984cd6a6ccbd2775201 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 14:18:41 +0800 Subject: [PATCH 54/80] fix: creation.failed --- options/locale/locale_en-US.ini | 2 +- routers/web/org/setting.go | 8 ++++++-- routers/web/repo/setting.go | 8 ++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3e76c929425e1..dc64bd585e0ee 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3221,7 +3221,7 @@ value = Value name = Name creation = Add Secret creation.success = The secret '%s' has been added. -creation.failed = Failed to remove secret. +creation.failed = Failed to add secret. deletion = Remove secret deletion.description = Removing a secret will revoke its access to repositories. Continue? deletion.success = The secret has been removed. diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index ca4cb592b1607..6df7f4ee0c44b 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -273,7 +273,9 @@ func SecretsPost(ctx *context.Context) { data, err := secret.EncryptSecret(setting.SecretKey, form.Content) if err != nil { - ctx.ServerError("EncryptSecret", err) + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("encrypt secret: %v", err) + ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") return } @@ -282,7 +284,9 @@ func SecretsPost(ctx *context.Context) { Name: form.Title, Data: data, }); err != nil { - ctx.ServerError("Insert Secret", err) + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("insert secret: %v", err) + ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") return } diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 1eb088b25ed45..0cf9f32b715e6 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1131,7 +1131,9 @@ func SecretsPost(ctx *context.Context) { data, err := secret.EncryptSecret(setting.SecretKey, form.Content) if err != nil { - ctx.ServerError("EncryptSecret", err) + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("encrypt secret: %v", err) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") return } @@ -1140,7 +1142,9 @@ func SecretsPost(ctx *context.Context) { Name: form.Title, Data: data, }); err != nil { - ctx.ServerError("Insert Secret", err) + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("insert secret: %v", err) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") return } From 7b241caec12cf465d6cdf56a5af1c0223450ed03 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 15:08:08 +0800 Subject: [PATCH 55/80] fix: check reg --- models/auth/secret.go | 6 +++--- routers/web/org/setting.go | 12 ++++++++++-- routers/web/repo/setting.go | 12 ++++++++++-- templates/org/settings/secrets.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 2 +- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index b4f0e753909cf..caacbe3295b63 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -34,8 +34,6 @@ func (err ErrSecretInvalidValue) Unwrap() error { return util.ErrInvalidArgument } -var nameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9-_.]*$") - // Secret represents a secret type Secret struct { ID int64 @@ -50,6 +48,8 @@ func init() { db.RegisterModel(new(Secret)) } +var secretNameReg = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_.-]*$") + // Validate validates the required fields and formats. func (s *Secret) Validate() error { switch { @@ -57,7 +57,7 @@ func (s *Secret) Validate() error { return ErrSecretInvalidValue{Name: &s.Name} case len(s.Data) == 0: return ErrSecretInvalidValue{Data: &s.Data} - case nameRE.MatchString(s.Name): + case !secretNameReg.MatchString(s.Name): return ErrSecretInvalidValue{Name: &s.Name} default: return nil diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 6df7f4ee0c44b..75daa7b81f3f5 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -279,11 +279,19 @@ func SecretsPost(ctx *context.Context) { return } - if err := db.Insert(ctx, &auth_model.Secret{ + sec := &auth_model.Secret{ OwnerID: ctx.Org.Organization.ID, Name: form.Title, Data: data, - }); err != nil { + } + if err := sec.Validate(); err != nil { + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("validate secret: %v", err) + ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") + return + } + + if err := db.Insert(ctx, sec); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("insert secret: %v", err) ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 0cf9f32b715e6..f7ec1f90688cf 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1137,11 +1137,19 @@ func SecretsPost(ctx *context.Context) { return } - if err := db.Insert(ctx, &auth_model.Secret{ + sec := &auth_model.Secret{ RepoID: ctx.Repo.Repository.ID, Name: form.Title, Data: data, - }); err != nil { + } + if err := sec.Validate(); err != nil { + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + log.Error("validate secret: %v", err) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") + return + } + + if err := db.Insert(ctx, sec); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("insert secret: %v", err) ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index da28bfa92b583..7775a3a6cb318 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -21,7 +21,7 @@
    - +
    diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index f5f0a65174476..0560e39e651c9 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -14,7 +14,7 @@
    - +
    From 031ac08828f047a53b2219d9b2b690912594b6d7 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 15:14:06 +0800 Subject: [PATCH 56/80] chore: remove tooltip --- templates/org/settings/secrets.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 7775a3a6cb318..9bf85e632aa12 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -45,7 +45,7 @@
    - {{svg "octicon-key" 32}} + {{svg "octicon-key" 32}}
    {{.Name}} diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index 0560e39e651c9..595d01fbb408d 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -38,7 +38,7 @@
    - {{svg "octicon-key" 32}} + {{svg "octicon-key" 32}}
    {{.Name}} From 4753a9b1b3c745c88f8993c40dba851f0ec3b848 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 15 Dec 2022 15:21:31 +0800 Subject: [PATCH 57/80] feat: trim space --- options/locale/locale_en-US.ini | 1 + routers/web/org/setting.go | 2 +- routers/web/repo/setting.go | 2 +- templates/org/settings/secrets.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index dc64bd585e0ee..33cb90152c380 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3220,6 +3220,7 @@ none = There are no secrets yet. value = Value name = Name creation = Add Secret +creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted. creation.success = The secret '%s' has been added. creation.failed = Failed to add secret. deletion = Remove secret diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 75daa7b81f3f5..39ae1fbcd7df3 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -271,7 +271,7 @@ func Secrets(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - data, err := secret.EncryptSecret(setting.SecretKey, form.Content) + data, err := secret.EncryptSecret(setting.SecretKey, strings.TrimSpace(form.Content)) if err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("encrypt secret: %v", err) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index f7ec1f90688cf..7bc4f79bd36f3 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1129,7 +1129,7 @@ func DeployKeys(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddKeyForm) - data, err := secret.EncryptSecret(setting.SecretKey, form.Content) + data, err := secret.EncryptSecret(setting.SecretKey, strings.TrimSpace(form.Content)) if err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("encrypt secret: %v", err) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 9bf85e632aa12..f09188b8d0e10 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -25,7 +25,7 @@
    - +
    - +
    From 3c26505331f442877d5cf061a20a1a7bd20a4408 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Mon, 19 Dec 2022 12:59:10 +0800 Subject: [PATCH 64/80] fix: label for --- templates/org/settings/secrets.tmpl | 8 ++++---- templates/repo/settings/secrets.tmpl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 38acd0cc07740..fe4b335186dc4 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -20,12 +20,12 @@ {{.locale.Tr "secrets.description"}}
    - - + +
    - - + +
    From 762a23cd5e3af43281f97a382f13e41c632b3bb2 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Mon, 19 Dec 2022 21:07:29 +0800 Subject: [PATCH 67/80] fix: update name rule --- models/auth/secret.go | 7 +++++-- options/locale/locale_en-US.ini | 1 + routers/web/org/setting.go | 3 ++- routers/web/repo/setting.go | 5 +++-- routers/web/web.go | 2 +- templates/org/settings/secrets.tmpl | 2 +- templates/repo/settings/secrets.tmpl | 2 +- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index 80190e7c4e368..76598416ce78a 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "regexp" + "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" @@ -48,7 +49,7 @@ func init() { db.RegisterModel(new(Secret)) } -var secretNameReg = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_.-]*$") +var secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$") // Validate validates the required fields and formats. func (s *Secret) Validate() error { @@ -57,7 +58,9 @@ func (s *Secret) Validate() error { return ErrSecretInvalidValue{Name: &s.Name} case len(s.Data) == 0: return ErrSecretInvalidValue{Data: &s.Data} - case !secretNameReg.MatchString(s.Name): + case !secretNameReg.MatchString(s.Name) || + strings.HasPrefix(s.Name, "GITHUB_") || + strings.HasPrefix(s.Name, "GITEA_"): return ErrSecretInvalidValue{Name: &s.Name} default: return nil diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 33cb90152c380..bf2ff41c8ec2a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3220,6 +3220,7 @@ none = There are no secrets yet. value = Value name = Name creation = Add Secret +creation.name_placeholder = Not case-sensitive, alphanumeric characters or underscores only. creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted. creation.success = The secret '%s' has been added. creation.failed = Failed to add secret. diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 39ae1fbcd7df3..2d1e400e872aa 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -278,10 +278,11 @@ func SecretsPost(ctx *context.Context) { ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") return } + name := strings.ToUpper(form.Title) sec := &auth_model.Secret{ OwnerID: ctx.Org.Organization.ID, - Name: form.Title, + Name: name, Data: data, } if err := sec.Validate(); err != nil { diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 7e05ca301bce7..015554aa72813 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1127,7 +1127,7 @@ func DeployKeys(ctx *context.Context) { // SecretsPost response for creating a new secret func SecretsPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.AddKeyForm) + form := web.GetForm(ctx).(*forms.AddSecretForm) data, err := secret.EncryptSecret(setting.SecretKey, strings.TrimSpace(form.Content)) if err != nil { @@ -1136,10 +1136,11 @@ func SecretsPost(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") return } + name := strings.ToUpper(form.Title) sec := &auth_model.Secret{ RepoID: ctx.Repo.Repository.ID, - Name: form.Title, + Name: name, Data: data, } if err := sec.Validate(); err != nil { diff --git a/routers/web/web.go b/routers/web/web.go index ea4905eea6319..20d067a163cea 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -919,7 +919,7 @@ func RegisterRoutes(m *web.Route) { Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost) m.Post("/delete", repo.DeleteDeployKey) m.Group("/secrets", func() { - m.Post("", web.Bind(forms.AddKeyForm{}), repo.SecretsPost) + m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost) m.Post("/delete", repo.DeleteSecret) }) }) diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index 04df1f3394843..dd2a437b75253 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -21,7 +21,7 @@
    - +
    diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index 0bf3357282ccb..23474c8a4da44 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -14,7 +14,7 @@
    - +
    From 1c415f9b00542f5262753c9c9e8cab5f51e0be83 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 21:29:55 +0800 Subject: [PATCH 68/80] Add a simple description for secrets --- docs/content/doc/secrets/overview.en-us.md | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/content/doc/secrets/overview.en-us.md diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md new file mode 100644 index 0000000000000..5516ce275458c --- /dev/null +++ b/docs/content/doc/secrets/overview.en-us.md @@ -0,0 +1,37 @@ +--- +date: "2022-12-19T21:26:00+08:00" +title: "Encrypted secrets" +slug: "secrets/overview" +draft: false +toc: false +menu: + sidebar: + parent: "secrets" + name: "Overview" + weight: 1 + identifier: "overview" +--- + +# Encrypted secrets + +Encrypted secrets allow you to store sensitive information in your organization, repository, or repository environments. + +# Naming your secrets + +The following rules apply to secret names: + +Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. + +Secret names must not start with the `GITHUB_` and `GITEA_` prefix. + +Secret names must not start with a number. + +Secret names are not case-sensitive. + +Secret names must be unique at the level they are created at. + +For example, a secret created at the environment level must have a unique name in that environment, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level. + +If a secret with the same name exists at multiple levels, the secret at the lowest level takes precedence. For example, if an organization-level secret has the same name as a repository-level secret, then the repository-level secret takes precedence. Similarly, if an organization, repository, and environment all have a secret with the same name, the environment-level secret takes precedence. + +To help ensure that GitHub redacts your secret in logs, avoid using structured data as the values of secrets. For example, avoid creating secrets that contain JSON or encoded Git blobs. From 0f9e2a68ce79e79b5bc4160976f975bc261884d9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 21:47:57 +0800 Subject: [PATCH 69/80] remove wrong description --- docs/content/doc/secrets/overview.en-us.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 5516ce275458c..11f57622b2394 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -30,8 +30,6 @@ Secret names are not case-sensitive. Secret names must be unique at the level they are created at. -For example, a secret created at the environment level must have a unique name in that environment, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level. +For example, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level. -If a secret with the same name exists at multiple levels, the secret at the lowest level takes precedence. For example, if an organization-level secret has the same name as a repository-level secret, then the repository-level secret takes precedence. Similarly, if an organization, repository, and environment all have a secret with the same name, the environment-level secret takes precedence. - -To help ensure that GitHub redacts your secret in logs, avoid using structured data as the values of secrets. For example, avoid creating secrets that contain JSON or encoded Git blobs. +If a secret with the same name exists at multiple levels, the secret at the lowest level takes precedence. For example, if an organization-level secret has the same name as a repository-level secret, then the repository-level secret takes precedence. Similarly, if an organization and repository both have a secret with the same name, the repository-level secret takes precedence. From 0ae9a050effb7c710c311edf28a8da44408cb86d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 21:50:54 +0800 Subject: [PATCH 70/80] Update docs/content/doc/secrets/overview.en-us.md Co-authored-by: delvh --- docs/content/doc/secrets/overview.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 11f57622b2394..86102612b6b8c 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -20,7 +20,7 @@ Encrypted secrets allow you to store sensitive information in your organization, The following rules apply to secret names: -Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. +Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]) or underscores (`_`). Spaces are not allowed. Secret names must not start with the `GITHUB_` and `GITEA_` prefix. From 5395a56c6072eacb198072d00bfbde58c736cacc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 21:54:37 +0800 Subject: [PATCH 71/80] Apply suggestions from code review Co-authored-by: delvh --- models/auth/secret.go | 8 +++++--- options/locale/locale_en-US.ini | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index 76598416ce78a..9224d240a12fd 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -49,7 +49,10 @@ func init() { db.RegisterModel(new(Secret)) } -var secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$") +var ( + secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$") + forbiddenSecretPrefixReg = regexp.MustCompile("^GIT(EA|HUB)_") + ) // Validate validates the required fields and formats. func (s *Secret) Validate() error { @@ -59,8 +62,7 @@ func (s *Secret) Validate() error { case len(s.Data) == 0: return ErrSecretInvalidValue{Data: &s.Data} case !secretNameReg.MatchString(s.Name) || - strings.HasPrefix(s.Name, "GITHUB_") || - strings.HasPrefix(s.Name, "GITEA_"): + forbiddenSecretPrefixReg.MatchSting(s.Name): return ErrSecretInvalidValue{Name: &s.Name} default: return nil diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bf2ff41c8ec2a..dd31562c3a7b7 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3220,7 +3220,7 @@ none = There are no secrets yet. value = Value name = Name creation = Add Secret -creation.name_placeholder = Not case-sensitive, alphanumeric characters or underscores only. +creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_ creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted. creation.success = The secret '%s' has been added. creation.failed = Failed to add secret. From 524561d5085009f8fc0827b61efe5d893c2c4c29 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 21:55:04 +0800 Subject: [PATCH 72/80] Update docs/content/doc/secrets/overview.en-us.md Co-authored-by: delvh --- docs/content/doc/secrets/overview.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 86102612b6b8c..3b05aabf073f5 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -20,7 +20,7 @@ Encrypted secrets allow you to store sensitive information in your organization, The following rules apply to secret names: -Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]) or underscores (`_`). Spaces are not allowed. +Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed. Secret names must not start with the `GITHUB_` and `GITEA_` prefix. From 4478b5ef800caed7cf7126f644a5809cd1e090d6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 22:07:25 +0800 Subject: [PATCH 73/80] Fix lint --- models/auth/secret.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/models/auth/secret.go b/models/auth/secret.go index 9224d240a12fd..7f1c21451ab81 100644 --- a/models/auth/secret.go +++ b/models/auth/secret.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "regexp" - "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" @@ -50,9 +49,9 @@ func init() { } var ( - secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$") + secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$") forbiddenSecretPrefixReg = regexp.MustCompile("^GIT(EA|HUB)_") - ) +) // Validate validates the required fields and formats. func (s *Secret) Validate() error { @@ -62,7 +61,7 @@ func (s *Secret) Validate() error { case len(s.Data) == 0: return ErrSecretInvalidValue{Data: &s.Data} case !secretNameReg.MatchString(s.Name) || - forbiddenSecretPrefixReg.MatchSting(s.Name): + forbiddenSecretPrefixReg.MatchString(s.Name): return ErrSecretInvalidValue{Name: &s.Name} default: return nil From 32cae8e6bd96065bdab7d3ea97ca9387de175470 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 22:34:55 +0800 Subject: [PATCH 74/80] Follow reviewers' requirements --- models/organization/org.go | 4 ++-- models/repo.go | 4 ++-- models/{auth => secret}/secret.go | 14 ++++++++++++-- routers/web/org/setting.go | 13 ++++--------- routers/web/repo/setting.go | 13 ++++--------- 5 files changed, 24 insertions(+), 24 deletions(-) rename models/{auth => secret}/secret.go (90%) diff --git a/models/organization/org.go b/models/organization/org.go index 1192ec7407686..9d9e9cda46a48 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -9,10 +9,10 @@ import ( "fmt" "strings" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" + secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -371,7 +371,7 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { &TeamUser{OrgID: org.ID}, &TeamUnit{OrgID: org.ID}, &TeamInvite{OrgID: org.ID}, - &auth_model.Secret{OwnerID: org.ID}, + &secret_model.Secret{OwnerID: org.ID}, ); err != nil { return fmt.Errorf("DeleteBeans: %w", err) } diff --git a/models/repo.go b/models/repo.go index d78a775e1cdc7..e95887077c955 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,7 +14,6 @@ import ( activities_model "code.gitea.io/gitea/models/activities" admin_model "code.gitea.io/gitea/models/admin" asymkey_model "code.gitea.io/gitea/models/asymkey" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -22,6 +21,7 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" + secret_model "code.gitea.io/gitea/models/secret" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -151,7 +151,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &admin_model.Task{RepoID: repoID}, &repo_model.Watch{RepoID: repoID}, &webhook.Webhook{RepoID: repoID}, - &auth_model.Secret{RepoID: repoID}, + &secret_model.Secret{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/models/auth/secret.go b/models/secret/secret.go similarity index 90% rename from models/auth/secret.go rename to models/secret/secret.go index 7f1c21451ab81..cd7f43b51ed3c 100644 --- a/models/auth/secret.go +++ b/models/secret/secret.go @@ -1,12 +1,13 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package auth +package secret import ( "context" "fmt" "regexp" + "strings" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" @@ -44,6 +45,15 @@ type Secret struct { CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } +func NewSecret(ownerID, repoID int64, name, data string) *Secret { + return &Secret{ + OwnerID: ownerID, + RepoID: repoID, + Name: strings.ToUpper(name), + Data: data, + } +} + func init() { db.RegisterModel(new(Secret)) } @@ -56,7 +66,7 @@ var ( // Validate validates the required fields and formats. func (s *Secret) Validate() error { switch { - case len(s.Name) == 0: + case len(s.Name) == 0 || len(s.Name) > 50: return ErrSecretInvalidValue{Name: &s.Name} case len(s.Data) == 0: return ErrSecretInvalidValue{Data: &s.Data} diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 2d1e400e872aa..c8b13996d65f8 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -10,9 +10,9 @@ import ( "strings" "code.gitea.io/gitea/models" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + secret_model "code.gitea.io/gitea/models/secret" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" @@ -257,7 +257,7 @@ func Secrets(ctx *context.Context) { ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsOrgSettingsSecrets"] = true - secrets, err := auth_model.FindSecrets(ctx, auth_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID}) + secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID}) if err != nil { ctx.ServerError("FindSecrets", err) return @@ -278,13 +278,8 @@ func SecretsPost(ctx *context.Context) { ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") return } - name := strings.ToUpper(form.Title) - sec := &auth_model.Secret{ - OwnerID: ctx.Org.Organization.ID, - Name: name, - Data: data, - } + sec := secret_model.NewSecret(ctx.Org.Organization.ID, 0, form.Title, data) if err := sec.Validate(); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) @@ -307,7 +302,7 @@ func SecretsPost(ctx *context.Context) { // SecretsDelete delete secrets func SecretsDelete(ctx *context.Context) { id := ctx.FormInt64("id") - if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { + if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil { ctx.Flash.Error(ctx.Tr("secrets.deletion.failed")) log.Error("delete secret %d: %v", id, err) } else { diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 015554aa72813..16dce4c657bdb 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -15,11 +15,11 @@ import ( "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" - auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" + secret_model "code.gitea.io/gitea/models/secret" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" @@ -1115,7 +1115,7 @@ func DeployKeys(ctx *context.Context) { } ctx.Data["Deploykeys"] = keys - secrets, err := auth_model.FindSecrets(ctx, auth_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID}) + secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("FindSecrets", err) return @@ -1136,13 +1136,8 @@ func SecretsPost(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") return } - name := strings.ToUpper(form.Title) - sec := &auth_model.Secret{ - RepoID: ctx.Repo.Repository.ID, - Name: name, - Data: data, - } + sec := secret_model.NewSecret(0, ctx.Repo.Repository.ID, form.Title, data) if err := sec.Validate(); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) @@ -1226,7 +1221,7 @@ func DeployKeysPost(ctx *context.Context) { func DeleteSecret(ctx *context.Context) { id := ctx.FormInt64("id") - if _, err := db.DeleteByBean(ctx, &auth_model.Secret{ID: id}); err != nil { + if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil { ctx.Flash.Error(ctx.Tr("secrets.deletion.failed")) log.Error("delete secret %d: %v", id, err) } else { From 5652a6d7dd48cb29e019d7a5f257d800972ada41 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 23:41:18 +0800 Subject: [PATCH 75/80] introduce InsertEncryptedSecret function --- models/secret/secret.go | 18 +++++++++++++++++- routers/web/org/setting.go | 18 +----------------- routers/web/repo/setting.go | 18 +----------------- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/models/secret/secret.go b/models/secret/secret.go index cd7f43b51ed3c..f970d5319e7e3 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -10,6 +10,8 @@ import ( "strings" "code.gitea.io/gitea/models/db" + secret_module "code.gitea.io/gitea/modules/secret" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -45,7 +47,8 @@ type Secret struct { CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } -func NewSecret(ownerID, repoID int64, name, data string) *Secret { +// newSecret Creates a new already encrypted secret +func newSecret(ownerID, repoID int64, name, data string) *Secret { return &Secret{ OwnerID: ownerID, RepoID: repoID, @@ -54,6 +57,19 @@ func NewSecret(ownerID, repoID int64, name, data string) *Secret { } } +// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database +func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) { + encrypted, err := secret_module.EncryptSecret(setting.SecretKey, strings.TrimSpace(data)) + if err != nil { + return nil, err + } + secret := newSecret(ownerID, repoID, name, encrypted) + if err := secret.Validate(); err != nil { + return secret, err + } + return secret, db.Insert(ctx, secret) +} + func init() { db.RegisterModel(new(Secret)) } diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index c8b13996d65f8..9fafd06fcc821 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" user_setting "code.gitea.io/gitea/routers/web/user/setting" @@ -271,29 +270,14 @@ func Secrets(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - data, err := secret.EncryptSecret(setting.SecretKey, strings.TrimSpace(form.Content)) + _, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, strings.TrimSpace(form.Content)) if err != nil { - ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) - log.Error("encrypt secret: %v", err) - ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") - return - } - - sec := secret_model.NewSecret(ctx.Org.Organization.ID, 0, form.Title, data) - if err := sec.Validate(); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") return } - if err := db.Insert(ctx, sec); err != nil { - ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) - log.Error("insert secret: %v", err) - ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") - return - } - log.Trace("Org %d: secret added", ctx.Org.Organization.ID) ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title)) ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets") diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 16dce4c657bdb..b44947f195f70 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -31,7 +31,6 @@ import ( "code.gitea.io/gitea/modules/log" mirror_module "code.gitea.io/gitea/modules/mirror" repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" @@ -1129,29 +1128,14 @@ func DeployKeys(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - data, err := secret.EncryptSecret(setting.SecretKey, strings.TrimSpace(form.Content)) + _, err := secret_model.InsertEncryptedSecret(ctx, 0, ctx.Repo.Repository.ID, form.Title, strings.TrimSpace(form.Content)) if err != nil { - ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) - log.Error("encrypt secret: %v", err) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") - return - } - - sec := secret_model.NewSecret(0, ctx.Repo.Repository.ID, form.Title, data) - if err := sec.Validate(); err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") return } - if err := db.Insert(ctx, sec); err != nil { - ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) - log.Error("insert secret: %v", err) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") - return - } - log.Trace("Secret added: %d", ctx.Repo.Repository.ID) ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title)) ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") From 8b9837af9fd386c79dffce0680d4fc1ada8ba950 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 23:49:27 +0800 Subject: [PATCH 76/80] Update docs/content/doc/secrets/overview.en-us.md Co-authored-by: delvh --- docs/content/doc/secrets/overview.en-us.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 3b05aabf073f5..90e896fea3e1b 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -14,7 +14,8 @@ menu: # Encrypted secrets -Encrypted secrets allow you to store sensitive information in your organization, repository, or repository environments. +Encrypted secrets allow you to store sensitive information in your organization or repository. +Secrets are available on Gitea 1.19+. # Naming your secrets From 4f266c16e5d34c48b97df582bb30f203674de28e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 19 Dec 2022 23:54:33 +0800 Subject: [PATCH 77/80] Fix lint --- docs/content/doc/secrets/overview.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 90e896fea3e1b..089c6f73ae6ca 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -14,7 +14,7 @@ menu: # Encrypted secrets -Encrypted secrets allow you to store sensitive information in your organization or repository. +Encrypted secrets allow you to store sensitive information in your organization or repository. Secrets are available on Gitea 1.19+. # Naming your secrets From e587971ece7b70b18a40638dbe0cb2971ef7ee3a Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 20 Dec 2022 14:30:24 +0800 Subject: [PATCH 78/80] chore: retrigger CI From 401b6a74d3d0d06c9160a056bc69f4a599f33e4b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 20 Dec 2022 16:16:31 +0800 Subject: [PATCH 79/80] Remove duplicated sentence --- docs/content/doc/secrets/overview.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 089c6f73ae6ca..1a88d6cfbcc6b 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -33,4 +33,4 @@ Secret names must be unique at the level they are created at. For example, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level. -If a secret with the same name exists at multiple levels, the secret at the lowest level takes precedence. For example, if an organization-level secret has the same name as a repository-level secret, then the repository-level secret takes precedence. Similarly, if an organization and repository both have a secret with the same name, the repository-level secret takes precedence. +If a secret with the same name exists at multiple levels, the secret at the lowest level takes precedence. For example, if an organization-level secret has the same name as a repository-level secret, then the repository-level secret takes precedence. From 0529b1c8a01a61c8f0b94e41d0227efb05f30419 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 20 Dec 2022 16:22:01 +0800 Subject: [PATCH 80/80] follow @KN4CK3R's review --- routers/web/org/setting.go | 2 +- routers/web/repo/setting.go | 2 +- templates/repo/settings/secrets.tmpl | 112 +++++++++++++-------------- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 9fafd06fcc821..e625962f7597a 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -270,7 +270,7 @@ func Secrets(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - _, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, strings.TrimSpace(form.Content)) + _, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, form.Content) if err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index b44947f195f70..913ed6c7cb8e9 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1128,7 +1128,7 @@ func DeployKeys(ctx *context.Context) { func SecretsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddSecretForm) - _, err := secret_model.InsertEncryptedSecret(ctx, 0, ctx.Repo.Repository.ID, form.Title, strings.TrimSpace(form.Content)) + _, err := secret_model.InsertEncryptedSecret(ctx, 0, ctx.Repo.Repository.ID, form.Title, form.Content) if err != nil { ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) log.Error("validate secret: %v", err) diff --git a/templates/repo/settings/secrets.tmpl b/templates/repo/settings/secrets.tmpl index 23474c8a4da44..6fb97beb4a85d 100644 --- a/templates/repo/settings/secrets.tmpl +++ b/templates/repo/settings/secrets.tmpl @@ -1,60 +1,60 @@
    -

    - {{.locale.Tr "secrets.secrets"}} -
    -
    {{.locale.Tr "secrets.creation"}}
    -
    -

    -
    -
    - - {{.CsrfTokenHtml}} -
    - {{.locale.Tr "secrets.description"}} -
    -
    - - -
    -
    - - -
    - - - -
    - {{if .Secrets}} -
    - {{range .Secrets}} -
    -
    - -
    -
    - {{svg "octicon-key" 32}} -
    -
    - {{.Name}} -
    ******
    -
    - - {{$.locale.Tr "settings.add_on"}} - {{.CreatedUnix.FormatShort}} - -
    -
    -
    - {{end}} +

    + {{.locale.Tr "secrets.secrets"}} +
    +
    {{.locale.Tr "secrets.creation"}}
    +
    +

    +
    +
    +
    + {{.CsrfTokenHtml}} +
    + {{.locale.Tr "secrets.description"}} +
    +
    + + +
    +
    + +
    - {{else}} - {{.locale.Tr "secrets.none"}} - {{end}} + + +
    + {{if .Secrets}} +
    + {{range .Secrets}} +
    +
    + +
    +
    + {{svg "octicon-key" 32}} +
    +
    + {{.Name}} +
    ******
    +
    + + {{$.locale.Tr "settings.add_on"}} + {{.CreatedUnix.FormatShort}} + +
    +
    +
    + {{end}} +
    + {{else}} + {{.locale.Tr "secrets.none"}} + {{end}}
    +