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 25 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
38 changes: 20 additions & 18 deletions models/auth/secret.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package auth

Expand All @@ -10,34 +9,37 @@ import (

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
)

type ErrSecretNameInvalid struct {
Name string
type ErrSecretInvalidValue struct {
Name *string
Data *string
}

func (err ErrSecretNameInvalid) Error() string {
return fmt.Sprintf("secret name %s is invalid", err.Name)
}

type ErrSecretDataInvalid struct {
Data string
func (err ErrSecretInvalidValue) Error() string {
if err.Name != nil {
return fmt.Sprintf("secret name %q is invalid", *err.Name)
}
if err.Data != nil {
return fmt.Sprintf("secret data %q is invalid", *err.Data)
}
return util.ErrInvalidArgument.Error()
}

func (err ErrSecretDataInvalid) Error() string {
return fmt.Sprintf("secret data %s is invalid", err.Data)
func (err ErrSecretInvalidValue) Unwrap() error {
return util.ErrInvalidArgument
}

var nameRE = regexp.MustCompile("[^a-zA-Z0-9-_.]+")
var nameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9-_.]*$")

// 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"`
Data string `xorm:"TEXT"`
PullRequest bool `xorm:"NOTNULL"`
Data string `xorm:"LONGTEXT"` // encrypted data, or plaintext data if there's no master key
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".

}

Expand All @@ -49,11 +51,11 @@ func init() {
func (s *Secret) Validate() error {
switch {
case len(s.Name) == 0:
return ErrSecretNameInvalid{Name: s.Name}
return ErrSecretInvalidValue{Name: &s.Name}
case len(s.Data) == 0:
return ErrSecretDataInvalid{Data: s.Data}
return ErrSecretInvalidValue{Data: &s.Data}
case nameRE.MatchString(s.Name):
return ErrSecretNameInvalid{Name: s.Name}
return ErrSecretInvalidValue{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
}
Expand Down
6 changes: 2 additions & 4 deletions models/migrations/v1_19/v236.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package v1_19 //nolint

Expand All @@ -16,8 +15,7 @@ func CreateSecretsTable(x *xorm.Engine) error {
UserID int64 `xorm:"index NOTNULL"`
RepoID int64 `xorm:"index NOTNULL"`
Name string `xorm:"NOTNULL"`
Data string `xorm:"TEXT"`
PullRequest bool `xorm:"NOTNULL"`
Data string `xorm:"LONGTEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOTNULL"`
}

Expand Down
10 changes: 4 additions & 6 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,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.`
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.`
Expand Down Expand Up @@ -2068,19 +2068,17 @@ 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
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
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?
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.
Expand Down
11 changes: 6 additions & 5 deletions routers/web/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -270,20 +270,21 @@ 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, form.PullRequestRead); err != nil {
ctx.ServerError("InsertRepoSecret", err)
if err := secret_service.InsertOrgSecret(ctx, ctx.Org.Organization.ID, form.Title, form.Content); err != nil {
ctx.ServerError("InsertOrgSecret", err)
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")
}

// SecretsDelete delete secrets
func SecretsDelete(ctx *context.Context) {
if err := secret_service.DeleteSecretByID(ctx, ctx.FormInt64("id")); err != nil {
ctx.Flash.Error("DeleteSecretByID: " + err.Error())
ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed"))
log.Error("delete secret %d: %v", ctx.FormInt64("id"), err)
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success"))
}
Expand Down
5 changes: 3 additions & 2 deletions routers/web/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
if err := secret_service.InsertRepoSecret(ctx, ctx.Repo.Repository.ID, form.Title, form.Content, form.PullRequestRead); err != nil {
if err := secret_service.InsertRepoSecret(ctx, ctx.Repo.Repository.ID, form.Title, form.Content); err != nil {
ctx.ServerError("InsertRepoSecret", err)
return
}
Expand Down Expand Up @@ -1206,7 +1206,8 @@ func DeployKeysPost(ctx *context.Context) {

func DeleteSecret(ctx *context.Context) {
if err := secret_service.DeleteSecretByID(ctx, ctx.FormInt64("id")); err != nil {
ctx.Flash.Error("DeleteSecretByID: " + err.Error())
ctx.Flash.Error(ctx.Tr("repo.settings.secret_deletion_failed"))
log.Error("delete secret %d: %v", ctx.FormInt64("id"), err)
} else {
ctx.Flash.Success(ctx.Tr("repo.settings.secret_deletion_success"))
}
Expand Down
2 changes: 1 addition & 1 deletion routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ func RegisterRoutes(m *web.Route) {

m.Group("/secrets", func() {
Copy link
Member

Choose a reason for hiding this comment

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

I'm almost in favor of setting context.OrgAssignment(true, true) here as a duplicate middleware simply to ensure that there is no chance ever that someone changes the permissions below and doesn't notice that the secrets are now publicly available.

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 it's unnecessary. Even if it does happen, the secret value is still not visible.

image

m.Get("", org.Secrets)
m.Post("", bindIgnErr(forms.AddSecretForm{}), org.SecretsPost)
m.Post("", web.Bind(forms.AddSecretForm{}), org.SecretsPost)
m.Post("/delete", org.SecretsDelete)
})

Expand Down
20 changes: 9 additions & 11 deletions services/forms/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -368,9 +367,8 @@ func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Er

// AddSecretForm for adding secrets
type AddSecretForm struct {
Title string `binding:"Required;MaxSize(50)"`
Content string `binding:"Required"`
PullRequestRead bool
Title string `binding:"Required;MaxSize(50)"`
Content string `binding:"Required"`
}

// Validate validates the fields
Expand Down
3 changes: 1 addition & 2 deletions services/secrets/encryption.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
5 changes: 2 additions & 3 deletions services/secrets/encryption_aes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down Expand Up @@ -59,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()]
Expand Down
3 changes: 1 addition & 2 deletions services/secrets/encryption_aes_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
3 changes: 1 addition & 2 deletions services/secrets/masterkey.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
3 changes: 1 addition & 2 deletions services/secrets/masterkey_nop.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
3 changes: 1 addition & 2 deletions services/secrets/masterkey_nop_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
3 changes: 1 addition & 2 deletions services/secrets/masterkey_plain.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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.
// SPDX-License-Identifier: MIT

package secrets

Expand Down
21 changes: 9 additions & 12 deletions services/secrets/secrets.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
Copy link
Member

Choose a reason for hiding this comment

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

Do we copyright the year the code was written, or the year it is merged?
I would have thought it is the year it is merged…

// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package secrets

Expand Down Expand Up @@ -106,29 +105,27 @@ func DecryptString(enc string) (string, error) {
return encProvider.DecryptString(enc, key)
}

func InsertRepoSecret(ctx context.Context, repoID int64, key, data string, pullRequest bool) error {
func InsertRepoSecret(ctx context.Context, repoID int64, key, data string) error {
v, err := EncryptString(data)
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 I've also mentioned it above, but here it is again:
I think it would be a good idea to strip leading and trailing whitespace from secrets.
There is only a marginal benefit in not doing it (who has a secret that starts or ends with whitespace?).
However, there is a huge benefit for usability, since many copying programs add for example whitespace at the end, meaning you would get an invalid secret that you first have to debug why it isn't working.

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.

if err != nil {
return err
}
return db.Insert(ctx, &auth_model.Secret{
RepoID: repoID,
Name: key,
Data: v,
PullRequest: pullRequest,
RepoID: repoID,
Name: key,
Data: v,
})
}

func InsertOrgSecret(ctx context.Context, userID int64, key, data string, pullRequest bool) error {
func InsertOrgSecret(ctx context.Context, userID int64, key, data string) error {
Copy link
Member

Choose a reason for hiding this comment

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

There is a name mismatch here: InsertOrgSecret and FindUserSecrets. I would rename both to Owner. Maybe change the structs member too.

Copy link
Member

Choose a reason for hiding this comment

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

I agree about the struct fields.
But we should find a name to stick with as OwnerSecret sounds abolutely wrong to me and doesn't clarify the difference to RepoSecrets enough.
What about UserSecrets instead, even if they can also be for an organization?

v, err := EncryptString(data)
if err != nil {
return err
}
return db.Insert(ctx, &auth_model.Secret{
UserID: userID,
Name: key,
Data: v,
PullRequest: pullRequest,
UserID: userID,
Name: key,
Data: v,
})
}

Expand Down
14 changes: 7 additions & 7 deletions templates/install.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,21 @@
<span class="help">{{.locale.Tr "install.enable_update_checker_helper"}}</span>
</div>

<!-- Security Settings -->
<h4 class="ui dividing header">{{.i18n.Tr "install.security_title"}}</h4>
<!-- Security Settings, especially MASTER_KEY -->
<h4 class="ui dividing header">{{.locale.Tr "install.security_title"}}</h4>

<div class="inline required field">
<label>{{.i18n.Tr "install.master_key_provider"}}</label>
<label>{{.locale.Tr "install.master_key_provider"}}</label>
<div class="ui selection master-key-provider dropdown">
<input type="hidden" name="master_key_provider" value="{{if .master_key_provider}}{{.master_key_provider}}{{else}}plain{{end}}">
<div class="text">{{.i18n.Tr "install.master_key_provider_plain"}}</div>
<div class="text">{{.locale.Tr "install.master_key_provider_plain"}}</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item" data-value="none">{{.i18n.Tr "install.master_key_provider_none"}}</div>
<div class="item" data-value="plain">{{.i18n.Tr "install.master_key_provider_plain"}}</div>
<div class="item" data-value="none">{{.locale.Tr "install.master_key_provider_none"}}</div>
<div class="item" data-value="plain">{{.locale.Tr "install.master_key_provider_plain"}}</div>
</div>
</div>
<span class="help">{{.i18n.Tr "install.master_key_provider_helper"}}</span>
<span class="help">{{.locale.Tr "install.master_key_provider_helper"}}</span>
</div>

<!-- Optional Settings -->
Expand Down
2 changes: 1 addition & 1 deletion templates/org/settings/navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<a class="{{if .PageIsOrgSettingsLabels}}active {{end}}item" href="{{.OrgLink}}/settings/labels">
{{.locale.Tr "repo.labels"}}
</a>
<a class="{{if .PageIsOrgSettingsSecrets}}active{{end}} item" href="{{.OrgLink}}/settings/secrets">
<a class="{{if .PageIsOrgSettingsSecrets}}active {{end}}item" href="{{.OrgLink}}/settings/secrets">
{{.locale.Tr "org.settings.secrets"}}
</a>
{{if .EnableOAuth2}}
Expand Down
Loading