Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Gitea secrets storage and management #14483

Closed
wants to merge 61 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
6b24ada
Add simple master key provider for secret encryption
lafriks Jan 5, 2021
47c472a
Add AES GCM encryption provider
lafriks Jan 7, 2021
64a91b4
add models and services
lunny Oct 7, 2022
9f2d204
improve UI
lunny Oct 9, 2022
368c97e
Merge main branch
lunny Oct 9, 2022
d39f7c4
finish adding and deleting secret for repository settting
lunny Oct 9, 2022
9465c13
org secrets
lunny Oct 10, 2022
eb2b139
fix show add secret panel
wxiaoguang Oct 13, 2022
f4df973
org secrets
lunny Oct 13, 2022
abde871
Fix duplicated alert
lunny Oct 14, 2022
b1e8915
delete modal
wxiaoguang Oct 14, 2022
a077ee2
Almost finished
lunny Oct 14, 2022
5b1044a
merge main branch
lunny Oct 14, 2022
834c7c1
Fix lint
lunny Oct 14, 2022
8e1291d
Some improvements
lunny Oct 15, 2022
1788de5
Merge branch 'main' into lafriks-fork-feat/secrets
lunny Oct 15, 2022
0a3be05
Fix bug
lunny Oct 15, 2022
a0ebf78
Merge branch 'main' into lafriks-fork-feat/secrets
lunny Oct 15, 2022
319da15
Fix bug
lunny Oct 16, 2022
b08114b
Merge branch 'main' into lafriks-fork-feat/secrets
lunny Oct 16, 2022
073656c
merge main branch
lunny Oct 17, 2022
f89bd80
merge main branch
lunny Oct 20, 2022
850d936
encrypt master key
lunny Oct 20, 2022
41310a7
merge main branch
lunny Oct 21, 2022
e54785a
merge main branch
lunny Oct 22, 2022
ccb57c8
merge main branch
lunny Oct 24, 2022
e30f532
merge main branch
lunny Nov 7, 2022
29e4f6b
merge main branch
lunny Nov 21, 2022
b01b2a9
merge main branch
lunny Nov 26, 2022
a1aee64
Merge branch 'main' into lafriks-fork-feat/secrets
lunny Nov 26, 2022
4c8f590
Merge branch 'main' into feature/secrets
wolfogre Dec 7, 2022
bc999bd
chore: add SPDX-License-Identifier
wolfogre Dec 7, 2022
41e9be0
fix: use .locale.Tr
wolfogre Dec 7, 2022
2c7ae0c
Merge branch 'main' into feature/secrets
wolfogre Dec 8, 2022
dd84d07
fix: use LONGTEXT
wolfogre Dec 9, 2022
f5effc1
fix: update nameRE
wolfogre Dec 9, 2022
c08fc15
fix: ErrSecretInvalidValue
wolfogre Dec 9, 2022
44ca6bf
fix: secret_deletion_failed
wolfogre Dec 9, 2022
6fcb7bf
fix: in_error
wolfogre Dec 9, 2022
b79b156
fix: remove PullRequest field
wolfogre Dec 9, 2022
acc0c12
fix: FindUserSecrets
wolfogre Dec 9, 2022
c754525
Merge pull request #4 from wolfogre/feature/secrets
lafriks Dec 9, 2022
eb5bcec
Merge branch 'main' into feat/secrets
wolfogre Dec 13, 2022
3183368
Update templates/org/settings/navbar.tmpl
wolfogre Dec 13, 2022
e86e30f
Update options/locale/locale_en-US.ini
wolfogre Dec 13, 2022
e6cee41
Update options/locale/locale_en-US.ini
wolfogre Dec 13, 2022
641d37a
fix: use web.Bind
wolfogre Dec 13, 2022
7c82f7a
fix: remove PullRequestRead
wolfogre Dec 13, 2022
f9d58d4
fix: rename to secret_name
wolfogre Dec 13, 2022
aa10928
Update templates/org/settings/secrets.tmpl
wolfogre Dec 13, 2022
f738069
Update routers/web/org/setting.go
wolfogre Dec 13, 2022
5103f1d
Update routers/web/org/setting.go
wolfogre Dec 13, 2022
a23241f
Update services/secrets/encryption_aes.go
wolfogre Dec 13, 2022
9f8fdaa
Update templates/install.tmpl
wolfogre Dec 13, 2022
b32bb7a
Update templates/repo/settings/secrets.tmpl
wolfogre Dec 13, 2022
23dd7a7
Merge branch 'main' into feat/secrets
wolfogre Dec 14, 2022
a8c192d
fix: rename to owner
wolfogre Dec 14, 2022
f1ef5ae
fix: remove FindObjects
wolfogre Dec 14, 2022
4a2676e
fix: add unique index
wolfogre Dec 14, 2022
5aa55fe
fix: delete secrets
wolfogre Dec 14, 2022
d1a729b
fix: generate master key with 64 chars
wolfogre Dec 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -32,6 +36,7 @@ var (
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
microcmdGenerateMasterKey,
},
}

Expand All @@ -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 {
Expand Down Expand Up @@ -99,3 +110,46 @@ 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
}
55 changes: 55 additions & 0 deletions models/auth/secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All new files don't conform with the new headers anymore:
Sometimes, the year is still 2021, and the other two lines should be replaced with the new header.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

And some files were actually created in 2021. It took too long to review.


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)
}
lunny marked this conversation as resolved.
Show resolved Hide resolved

var nameRE = regexp.MustCompile("[^a-zA-Z0-9-_.]+")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we perhaps allow @ and # as well, and disallow a leading - at the beginning?
This will probably cause far fewer problems long term…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @ and # are unnecessary for a name. Maybe ^[a-zA-Z_][a-zA-Z0-9-_.]*$ is good enouth.

What GitHub does:

Failed to add secret. Secret names can only contain alphanumeric characters ([a-z], [A-Z], [0-9]) or underscores (_). Spaces are not allowed. Must start with a letter ([a-z], [A-Z]) or underscores (_).


// Secret represents a secret
type Secret struct {
ID int64
lunny marked this conversation as resolved.
Show resolved Hide resolved
UserID int64 `xorm:"index NOTNULL"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OwnerID?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

RepoID int64 `xorm:"index NOTNULL"`
Name string `xorm:"NOTNULL"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed: If we change our preconditions slightly, we can also do something more advanced:
If we always set the user ID even on org secrets, then an org secret is simply a secret with repoID == 0.
This has the benefit that we can then enforce a combined uniqueness constraint over the userID, repoID, and the name.
This would have the advantage that you cannot have more than one secret for the same user-repo combi, so no global or repo specific secret could have a duplicate name.
I think that's a check we cannot omit, as the current implementation allows for inconsistent states when multiple secrets have the same name.


Let's test that there are no drawbacks on both possible values: Assume we have a secret name in org 1.

  • the org secret would be 1, 0, name
  • the repo secret for a specific repo, i.e. 2, would be 1, 2, name, so it doesn't violate the constraint.

the only thing to keep in mind when collecting the secrets is that we need to query the org secrets first so that the repo secrets will override the org secrets.


One other thing I noticed: We should also delete repo secrets when a repo is deleted and org secrets when an org is deleted.
Should hopefully be a two-liner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it be that the suggestions in this file were missed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enforce a combined uniqueness constraint over the userID, repoID, and the name.

I agree, but I think your example is incorrect. The repo secret for a specific repo, i.e. 2, would be 0, 2, name, not 1, 2, name. Anyway, the conclusion is the same.

delete repo secrets when a repo is deleted and org secrets when an org is deleted.

Agree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

Copy link
Member

@delvh delvh Dec 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right, but I'm not completely sure.
I forgot that the repoID also tells us implicitly which owner a repo belongs to, so we don't even need to set the owner ID.
So, we don't need to change our preconditions anymore and they are fine as they are.
(I'm speaking of

I just noticed: If we change our preconditions slightly, we can also do something more advanced:
If we always set the user ID even on org secrets, then an org secret is simply a secret with repoID == 0.
This has the benefit that we can then enforce a combined uniqueness constraint over the userID, repoID, and the name.

)

Data string `xorm:"TEXT"`
lunny marked this conversation as resolved.
Show resolved Hide resolved
PullRequest bool `xorm:"NOTNULL"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure what this attribute is supposed to be:
Why would we need that?
What would a PR-specific secret be?
Or how is that meant?
And, if we do need it: I don't think it is a good idea to limit it to 0 and 1 only, it's probably a better idea to define a separate type SecretType int { GlobalSecret SecretType = iota; PullSecret = 1; …}, or to use a separate permissions table for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This column indicates whether a pull request could read the secret. I think we could have two types CommitType and PullRequestType according your thought.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may use secrets outside of the git scope. A PullRequest column looks wrong then. The same could be said about the RepoID.

Copy link
Member

@delvh delvh Dec 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the PullRequest.
It seems like we need a more advanced access control system, or we don't use any permissions there at all, just like GitHub does.
This is one of the cases, where it is probably absolutely irrelevant, so we could even simplify the logic even more by removing the PR attributes.

However, I disagree with the RepoID, as if it is not present, it is simply an org secret instead of a repo secret, and a repo secret is still an often needed thing.
Our highest-level concepts are still users/orgs and repos (and issues to some extent, but they are irrelevant here). All of them are included in the current implementation…

Copy link
Member

@wolfogre wolfogre Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to add Type field for secrets, the PullRequest isn't a type, it indicates whether it can be used in CICD tasks triggered by pull requests, so I prefer something like:

type Secret struct {
	// ...
	ReadByPullRequest bool
	ReadByExternalAPI bool // for example
	ReadByXXX bool // then add as needed
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, let's copy someone's homework, maybe the field is unnecessary.

image

See: Using encrypted secrets in a workflow

CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also store UpdatedUnix?
Or should we perhaps only store UpdatedUnix?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, we can only add or remove a secret, so there's no "updated time".

}

// 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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and given my suggestion above, the case case s.UserID == 0 should also return an error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? If it's a repo secret, the OwnerID(aka UserID) should be 0. So the secret has nothing to do with the repo's owner, and works well with repo transferring.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, since we don't need to change our assumptions anymore, that's not needed.

return nil
}
}
18 changes: 18 additions & 0 deletions models/db/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

package db

import (
"context"

"xorm.io/builder"
)

// SearchOrderBy is used to sort the result
type SearchOrderBy string

Expand All @@ -28,3 +34,15 @@ 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 {
lunny marked this conversation as resolved.
Show resolved Hide resolved
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)
}
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ var migrations = []Migration{
NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
// v226 -> v227
NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
// v227 -> v228
NewMigration("Create secrets table", createSecretsTable),
}

// GetCurrentDBVersion returns the current db version
Expand Down
25 changes: 25 additions & 0 deletions models/migrations/v227.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 migrations

import (
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/xorm"
)

func createSecretsTable(x *xorm.Engine) error {
type Secret struct {
ID int64
UserID int64 `xorm:"index NOTNULL"`
RepoID int64 `xorm:"index NOTNULL"`
Name string `xorm:"NOTNULL"`
Data string `xorm:"TEXT"`
PullRequest bool `xorm:"NOTNULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"`
}

return x.Sync(new(Secret))
}
5 changes: 5 additions & 0 deletions modules/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ 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(32)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so short when the SecretKey already has 64 runes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved. Actually, I don't know why it was 32, but I think you're right and it makes sense to have the same length as SecretKey.

}
16 changes: 16 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ var (
HMACKey string `ini:"HMAC_KEY"`
Allways bool
}{}
MasterKeyProvider string
MasterKey []byte
Comment on lines +219 to +220
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those two config options are currently missing from config-cheat-sheet.
Or are they supposed to be undocumented?


// UI settings
UI = struct {
Expand Down Expand Up @@ -954,6 +956,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":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 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(",")
Expand Down
3 changes: 3 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ func NewFuncMap() []template.FuncMap {
return items
},
"HasPrefix": strings.HasPrefix,
"Shadow": func(s string) string {
return "******"
},
}}
}

Expand Down
7 changes: 7 additions & 0 deletions modules/web/middleware/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down
23 changes: 23 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 '[email protected]' 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
Expand Down Expand Up @@ -450,6 +457,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.`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in_error = ` can contain only specific values: %s.`
in_error = ` can only contain specific values: %s.`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved.

glob_pattern_error = ` glob pattern is invalid: %s.`
regex_pattern_error = ` regex pattern is invalid: %s.`
unknown_error = Unknown error:
Expand Down Expand Up @@ -2036,6 +2044,20 @@ 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 <strong>push</strong> to the repository.
settings.no_deploy_keys = There are no deploy keys yet.
settings.secrets = Secrets
settings.pull_request_read = Pull Request Read
settings.pull_request_read_info = "If allow pull request read the secret, it's security related."
settings.pull_request_read_hint = Allow pull request read the secret
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines do not sound like English:

UI Screenshot

image

I have absolutely no idea what they are even supposed to mean, so I can't suggest alternatives.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved, by removing the checkbox.

settings.add_secret = Add Secret
settings.add_secret_success = The secret '%s' has been added.
settings.secret_value_content_placeholder = Input any content
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that placeholder redundant?
It doesn't serve any purpose.
It would only have a purpose if it clarified for example how surounding whitespace is handled.

Speaking of which: Do we strip surrounding whitespace already? I hope so as that is in general much more user-friendly…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming my suggestion above is implemented:

Suggested change
settings.secret_value_content_placeholder = Input any content
settings.secret_value_content_placeholder = Input any content. Whitespace at the start and end will be omitted.

Then suddenly this placeholder isn't redundant anymore.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved in the new PR.

settings.secret_desc = Secrets could be visited by repository events
wolfogre marked this conversation as resolved.
Show resolved Hide resolved
settings.secret_content = Value
settings.secret_key = Key
wolfogre marked this conversation as resolved.
Show resolved Hide resolved
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.title = Title
settings.deploy_key_content = Content
settings.key_been_used = A deploy key with identical content is already in use.
Expand Down Expand Up @@ -2355,6 +2377,7 @@ 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
lunny marked this conversation as resolved.
Show resolved Hide resolved
settings.delete = Delete Organization
settings.delete_account = Delete This Organization
settings.delete_prompt = The organization will be permanently removed. This <strong>CANNOT</strong> be undone!
Expand Down
3 changes: 3 additions & 0 deletions routers/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ 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"
)
Expand Down Expand Up @@ -139,6 +140,8 @@ func GlobalInitInstalled(ctx context.Context) {
models.NewRepoContext()
mustInit(repo_service.Init)

mustInit(secret_service.Init)

// Booting long running goroutines.
issue_indexer.InitIssueIndexer(false)
code_indexer.Init()
Expand Down
Loading