From 7fd5d38860a9aef1a2ba3b30e882492fde84c9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Tue, 31 Jan 2023 22:43:06 -0300 Subject: [PATCH 01/21] Improve checkbox accessibility a bit by adding the title attribute (#22593) EDIT: The main change of this PR was resolved by #22599. This complements that PR for some cases without label and complicated layout to be added. NOTE: Contributed by @Forgejo. --- options/locale/locale_en-US.ini | 3 +++ templates/admin/config.tmpl | 4 ++-- templates/org/team/new.tmpl | 8 ++++---- templates/repo/editor/commit_form.tmpl | 2 +- templates/repo/issue/list.tmpl | 3 +-- templates/shared/issuelist.tmpl | 3 +-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3495afe859d0..de80a710e97e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1131,6 +1131,7 @@ editor.commit_directly_to_this_branch = Commit directly to the %[3]s pulls.merged_by = by %[3]s was merged %[1]s pulls.merged_by_fake = by %[2]s was merged %[1]s diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 982cfb280081..c2ab31862d97 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -303,14 +303,14 @@
{{.locale.Tr "admin.config.disable_gravatar"}}
- +
{{.locale.Tr "admin.config.enable_federated_avatar"}}
- +
diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index 10b5abda3145..005d7ce4e510 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -78,7 +78,7 @@ {{.locale.Tr "units.unit"}} {{.locale.Tr "org.teams.none_access"}} - {{svg "octicon-question" 16 "ml-2"}} + {{svg "octicon-question" 16 "ml-2"}} {{.locale.Tr "org.teams.read_access"}} {{svg "octicon-question" 16 "ml-2"}} {{.locale.Tr "org.teams.write_access"}} @@ -99,17 +99,17 @@
- +
- +
- +
diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index 8700f7740151..f4778e22d122 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -61,7 +61,7 @@
{{svg "octicon-git-branch"}} - +
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 22a6104baa88..b9ae36201678 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -30,8 +30,7 @@
{{if $.CanWriteIssuesOrPulls}}
- - +
{{end}} {{template "repo/issue/openclose" .}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index f3aa2610bb1a..93d54930ffdd 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -5,8 +5,7 @@
{{if $.CanWriteIssuesOrPulls}}
- - +
{{end}}
From 2871ea08096cba15546f357d0ec473734ee9d8be Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 1 Feb 2023 13:32:46 +0800 Subject: [PATCH 02/21] Add more events details supports for actions (#22680) #21937 implemented only basic events based on name because of `act`'s limitation. So I sent a PR to parse all possible events details in https://gitea.com/gitea/act/pulls/11 and it merged. The ref documentation is https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows This PR depends on that and make more detail responses for `push` events and `pull_request` events. And it lefts more events there for future PRs. --------- Co-authored-by: Jason Song --- go.mod | 2 +- go.sum | 4 +- modules/actions/workflows.go | 154 +++++++++++++++++++++++++++- services/actions/notifier_helper.go | 2 +- 4 files changed, 154 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 5ab0f4b27d3d..b3d40403235b 100644 --- a/go.mod +++ b/go.mod @@ -284,7 +284,7 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142 replace github.com/blevesearch/zapx/v15 v15.3.6 => github.com/zeripath/zapx/v15 v15.3.6-alignment-fix -replace github.com/nektos/act => gitea.com/gitea/act v0.234.0 +replace github.com/nektos/act => gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744 exclude github.com/gofrs/uuid v3.2.0+incompatible diff --git a/go.sum b/go.sum index 12042dd6fdb3..864ddff1cdad 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsi dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.234.0 h1:gWgMPMKdNcMrp/o2CF/SyVKiiJLBFl+xmzfvoHCpykU= -gitea.com/gitea/act v0.234.0/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14= +gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744 h1:cqzKmGlX0wynSXO04NILpL25eBGwogDrKpkkbwmIpj4= +gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14= gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4= gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index d340a1aa658a..c1c8e71f53b5 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -10,8 +10,11 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" + "github.com/gobwas/glob" + "github.com/nektos/act/pkg/jobparser" "github.com/nektos/act/pkg/model" ) @@ -41,7 +44,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) { return ret, nil } -func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (map[string][]byte, error) { +func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) { entries, err := ListWorkflows(commit) if err != nil { return nil, err @@ -63,13 +66,156 @@ func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (ma log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) continue } - for _, e := range workflow.On() { - if e == event.Event() { + events, err := jobparser.ParseRawOn(&workflow.RawOn) + if err != nil { + log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) + continue + } + for _, evt := range events { + if evt.Name != triggedEvent.Event() { + continue + } + + if detectMatched(commit, triggedEvent, payload, evt) { workflows[entry.Name()] = content - break } } } return workflows, nil } + +func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool { + if len(evt.Acts) == 0 { + return true + } + + switch triggedEvent { + case webhook_module.HookEventCreate: + fallthrough + case webhook_module.HookEventDelete: + fallthrough + case webhook_module.HookEventFork: + log.Warn("unsupported event %q", triggedEvent.Event()) + return false + case webhook_module.HookEventPush: + pushPayload := payload.(*api.PushPayload) + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts { + switch cond { + case "branches", "tags": + for _, val := range vals { + if glob.MustCompile(val, '/').Match(pushPayload.Ref) { + matchTimes++ + break + } + } + case "paths": + filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) + } else { + for _, val := range vals { + matched := false + for _, file := range filesChanged { + if glob.MustCompile(val, '/').Match(file) { + matched = true + break + } + } + if matched { + matchTimes++ + break + } + } + } + default: + log.Warn("unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts) + + case webhook_module.HookEventIssues: + fallthrough + case webhook_module.HookEventIssueAssign: + fallthrough + case webhook_module.HookEventIssueLabel: + fallthrough + case webhook_module.HookEventIssueMilestone: + fallthrough + case webhook_module.HookEventIssueComment: + fallthrough + case webhook_module.HookEventPullRequest: + prPayload := payload.(*api.PullRequestPayload) + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts { + switch cond { + case "types": + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(prPayload.Action)) { + matchTimes++ + break + } + } + case "branches": + for _, val := range vals { + if glob.MustCompile(val, '/').Match(prPayload.PullRequest.Base.Ref) { + matchTimes++ + break + } + } + case "paths": + filesChanged, err := commit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) + } else { + for _, val := range vals { + matched := false + for _, file := range filesChanged { + if glob.MustCompile(val, '/').Match(file) { + matched = true + break + } + } + if matched { + matchTimes++ + break + } + } + } + default: + log.Warn("unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts) + case webhook_module.HookEventPullRequestAssign: + fallthrough + case webhook_module.HookEventPullRequestLabel: + fallthrough + case webhook_module.HookEventPullRequestMilestone: + fallthrough + case webhook_module.HookEventPullRequestComment: + fallthrough + case webhook_module.HookEventPullRequestReviewApproved: + fallthrough + case webhook_module.HookEventPullRequestReviewRejected: + fallthrough + case webhook_module.HookEventPullRequestReviewComment: + fallthrough + case webhook_module.HookEventPullRequestSync: + fallthrough + case webhook_module.HookEventWiki: + fallthrough + case webhook_module.HookEventRepository: + fallthrough + case webhook_module.HookEventRelease: + fallthrough + case webhook_module.HookEventPackage: + fallthrough + default: + log.Warn("unsupported event %q", triggedEvent.Event()) + } + return false +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 9c9fc644cb49..5b8f6bfdf6ef 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -137,7 +137,7 @@ func notify(ctx context.Context, input *notifyInput) error { return fmt.Errorf("gitRepo.GetCommit: %w", err) } - workflows, err := actions_module.DetectWorkflows(commit, input.Event) + workflows, err := actions_module.DetectWorkflows(commit, input.Event, input.Payload) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } From 19d5b2f922c2defde579a935fbedb680eb8fff18 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 1 Feb 2023 07:24:10 +0000 Subject: [PATCH 03/21] Fix bugs with WebAuthn preventing sign in and registration. (#22651) This PR fixes two bugs with Webauthn support: * There was a longstanding bug within webauthn due to the backend using URLEncodedBase64 but the javascript using decoding using plain base64. This causes intermittent issues with users reporting decoding errors. * Following the recent upgrade to webauthn there was a change in the way the library expects RPOrigins to be configured. This leads to the Relying Party Origin not being configured and prevents registration. Fix #22507 Signed-off-by: Andrew Thornton Co-authored-by: wxiaoguang --- modules/auth/webauthn/webauthn.go | 2 +- modules/auth/webauthn/webauthn_test.go | 4 +-- web_src/js/features/user-auth-webauthn.js | 37 ++++++++++++++--------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/modules/auth/webauthn/webauthn.go b/modules/auth/webauthn/webauthn.go index d08f7bf7cc71..937da872ca1a 100644 --- a/modules/auth/webauthn/webauthn.go +++ b/modules/auth/webauthn/webauthn.go @@ -28,7 +28,7 @@ func Init() { Config: &webauthn.Config{ RPDisplayName: setting.AppName, RPID: setting.Domain, - RPOrigin: appURL, + RPOrigins: []string{appURL}, AuthenticatorSelection: protocol.AuthenticatorSelection{ UserVerification: "discouraged", }, diff --git a/modules/auth/webauthn/webauthn_test.go b/modules/auth/webauthn/webauthn_test.go index 1beeb64cd6ca..15a8d7182833 100644 --- a/modules/auth/webauthn/webauthn_test.go +++ b/modules/auth/webauthn/webauthn_test.go @@ -15,11 +15,11 @@ func TestInit(t *testing.T) { setting.Domain = "domain" setting.AppName = "AppName" setting.AppURL = "https://domain/" - rpOrigin := "https://domain" + rpOrigin := []string{"https://domain"} Init() assert.Equal(t, setting.Domain, WebAuthn.Config.RPID) assert.Equal(t, setting.AppName, WebAuthn.Config.RPDisplayName) - assert.Equal(t, rpOrigin, WebAuthn.Config.RPOrigin) + assert.Equal(t, rpOrigin, WebAuthn.Config.RPOrigins) } diff --git a/web_src/js/features/user-auth-webauthn.js b/web_src/js/features/user-auth-webauthn.js index f11a49864ded..9c9fffd99527 100644 --- a/web_src/js/features/user-auth-webauthn.js +++ b/web_src/js/features/user-auth-webauthn.js @@ -14,9 +14,9 @@ export function initUserAuthWebAuthn() { $.getJSON(`${appSubUrl}/user/webauthn/assertion`, {}) .done((makeAssertionOptions) => { - makeAssertionOptions.publicKey.challenge = decode(makeAssertionOptions.publicKey.challenge); + makeAssertionOptions.publicKey.challenge = decodeURLEncodedBase64(makeAssertionOptions.publicKey.challenge); for (let i = 0; i < makeAssertionOptions.publicKey.allowCredentials.length; i++) { - makeAssertionOptions.publicKey.allowCredentials[i].id = decode(makeAssertionOptions.publicKey.allowCredentials[i].id); + makeAssertionOptions.publicKey.allowCredentials[i].id = decodeURLEncodedBase64(makeAssertionOptions.publicKey.allowCredentials[i].id); } navigator.credentials.get({ publicKey: makeAssertionOptions.publicKey @@ -56,14 +56,14 @@ function verifyAssertion(assertedCredential) { type: 'POST', data: JSON.stringify({ id: assertedCredential.id, - rawId: bufferEncode(rawId), + rawId: encodeURLEncodedBase64(rawId), type: assertedCredential.type, clientExtensionResults: assertedCredential.getClientExtensionResults(), response: { - authenticatorData: bufferEncode(authData), - clientDataJSON: bufferEncode(clientDataJSON), - signature: bufferEncode(sig), - userHandle: bufferEncode(userHandle), + authenticatorData: encodeURLEncodedBase64(authData), + clientDataJSON: encodeURLEncodedBase64(clientDataJSON), + signature: encodeURLEncodedBase64(sig), + userHandle: encodeURLEncodedBase64(userHandle), }, }), contentType: 'application/json; charset=utf-8', @@ -85,14 +85,21 @@ function verifyAssertion(assertedCredential) { }); } -// Encode an ArrayBuffer into a base64 string. -function bufferEncode(value) { +// Encode an ArrayBuffer into a URLEncoded base64 string. +function encodeURLEncodedBase64(value) { return encode(value) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } +// Dccode a URLEncoded base64 to an ArrayBuffer string. +function decodeURLEncodedBase64(value) { + return decode(value + .replace(/_/g, '/') + .replace(/-/g, '+')); +} + function webauthnRegistered(newCredential) { const attestationObject = new Uint8Array(newCredential.response.attestationObject); const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); @@ -104,11 +111,11 @@ function webauthnRegistered(newCredential) { headers: {'X-Csrf-Token': csrfToken}, data: JSON.stringify({ id: newCredential.id, - rawId: bufferEncode(rawId), + rawId: encodeURLEncodedBase64(rawId), type: newCredential.type, response: { - attestationObject: bufferEncode(attestationObject), - clientDataJSON: bufferEncode(clientDataJSON), + attestationObject: encodeURLEncodedBase64(attestationObject), + clientDataJSON: encodeURLEncodedBase64(clientDataJSON), }, }), dataType: 'json', @@ -184,11 +191,11 @@ function webAuthnRegisterRequest() { }).done((makeCredentialOptions) => { $('#nickname').closest('div.field').removeClass('error'); - makeCredentialOptions.publicKey.challenge = decode(makeCredentialOptions.publicKey.challenge); - makeCredentialOptions.publicKey.user.id = decode(makeCredentialOptions.publicKey.user.id); + makeCredentialOptions.publicKey.challenge = decodeURLEncodedBase64(makeCredentialOptions.publicKey.challenge); + makeCredentialOptions.publicKey.user.id = decodeURLEncodedBase64(makeCredentialOptions.publicKey.user.id); if (makeCredentialOptions.publicKey.excludeCredentials) { for (let i = 0; i < makeCredentialOptions.publicKey.excludeCredentials.length; i++) { - makeCredentialOptions.publicKey.excludeCredentials[i].id = decode(makeCredentialOptions.publicKey.excludeCredentials[i].id); + makeCredentialOptions.publicKey.excludeCredentials[i].id = decodeURLEncodedBase64(makeCredentialOptions.publicKey.excludeCredentials[i].id); } } From 72a83dcc82add7537d2f661aa929dd073ced65f3 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 1 Feb 2023 17:14:40 +0900 Subject: [PATCH 04/21] Explain that the no-access team unit does not affect public repositories (#22661) Fixes https://github.com/go-gitea/gitea/issues/22600 Add explanations to team unit access control. --------- Co-authored-by: Jason Song --- 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 de80a710e97e..24809c55495e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2432,7 +2432,7 @@ teams.leave.detail = Leave %s? teams.can_create_org_repo = Create repositories teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository. teams.none_access = No Access -teams.none_access_helper = Members cannot view or do any other action on this unit. +teams.none_access_helper = Members cannot view or do any other action on this unit. It has no effect for public repositories. teams.general_access = General Access teams.general_access_helper = Members permissions will be decided by below permission table. teams.read_access = Read From 9f9a1ce92292739c3d0b5ee4bb654d883eb3b869 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 1 Feb 2023 11:48:35 +0000 Subject: [PATCH 05/21] Add missing close bracket in imagediff (#22710) There was a missing `]` in imagediff.js: ``` const $range = $container.find("input[type='range'"); ``` This PR simply adds this. Fix #22702 --- web_src/js/features/imagediff.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/imagediff.js b/web_src/js/features/imagediff.js index 03ae3b047bf8..250ea957981f 100644 --- a/web_src/js/features/imagediff.js +++ b/web_src/js/features/imagediff.js @@ -263,7 +263,7 @@ export function initImageDiff() { height: sizes.max.height * factor + 4 }); - const $range = $container.find("input[type='range'"); + const $range = $container.find("input[type='range']"); const onInput = () => sizes.image1.parent().css({ opacity: $range.val() / 100 }); From 5882e179a93a00a0635c6c578ec6d43ce68d687b Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 1 Feb 2023 13:53:04 +0100 Subject: [PATCH 06/21] Add user secrets (#22191) Fixes #22183 Replaces #22187 This PR adds secrets for users. I refactored the files for organizations and repos to use the same logic and templates. I splitted the secrets from deploy keys again and reverted the fix from #22187. --------- Co-authored-by: Lunny Xiao --- docs/content/doc/secrets/overview.en-us.md | 16 ++-- options/locale/locale_en-US.ini | 2 +- routers/web/org/setting.go | 51 ------------- routers/web/org/setting_secrets.go | 48 ++++++++++++ routers/web/repo/setting.go | 39 ---------- routers/web/repo/setting_secrets.go | 46 ++++++++++++ routers/web/shared/secrets/secrets.go | 54 ++++++++++++++ routers/web/user/setting/secrets.go | 45 +++++++++++ routers/web/web.go | 15 +++- templates/org/settings/secrets.tmpl | 70 +----------------- templates/repo/settings/deploy_keys.tmpl | 6 +- templates/repo/settings/nav.tmpl | 3 +- templates/repo/settings/navbar.tmpl | 3 + templates/repo/settings/secrets.tmpl | 86 ++-------------------- templates/shared/secrets/add_list.tmpl | 68 +++++++++++++++++ templates/user/settings/navbar.tmpl | 3 + templates/user/settings/secrets.tmpl | 10 +++ 17 files changed, 310 insertions(+), 255 deletions(-) create mode 100644 routers/web/org/setting_secrets.go create mode 100644 routers/web/repo/setting_secrets.go create mode 100644 routers/web/shared/secrets/secrets.go create mode 100644 routers/web/user/setting/secrets.go create mode 100644 templates/shared/secrets/add_list.tmpl create mode 100644 templates/user/settings/secrets.tmpl diff --git a/docs/content/doc/secrets/overview.en-us.md b/docs/content/doc/secrets/overview.en-us.md index 1a88d6cfbcc6..21fb65f98d3d 100644 --- a/docs/content/doc/secrets/overview.en-us.md +++ b/docs/content/doc/secrets/overview.en-us.md @@ -1,6 +1,6 @@ --- date: "2022-12-19T21:26:00+08:00" -title: "Encrypted secrets" +title: "Secrets" slug: "secrets/overview" draft: false toc: false @@ -12,24 +12,24 @@ menu: identifier: "overview" --- -# Encrypted secrets +# Secrets -Encrypted secrets allow you to store sensitive information in your organization or repository. +Secrets allow you to store sensitive information in your user, organization or repository. Secrets are available on Gitea 1.19+. # 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 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 the `GITHUB_` and `GITEA_` prefix. -Secret names must not start with a number. +- Secret names must not start with a number. -Secret names are not case-sensitive. +- Secret names are not case-sensitive. -Secret names must be unique at the level they are created at. +- 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. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 24809c55495e..8a48a68b171c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3254,7 +3254,7 @@ creation.value_placeholder = Input any content. Whitespace at the start and end creation.success = The secret '%s' has been added. creation.failed = Failed to add secret. deletion = Remove secret -deletion.description = Removing a secret will revoke its access to repositories. Continue? +deletion.description = Removing a secret is permanent and cannot be undone. Continue? deletion.success = The secret has been removed. deletion.failed = Failed to remove secret. diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 80f57072455b..5c9b7967c3dc 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models" "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" @@ -38,8 +37,6 @@ const ( tplSettingsHooks base.TplName = "org/settings/hooks" // tplSettingsLabels template path for render labels settings tplSettingsLabels base.TplName = "org/settings/labels" - // tplSettingsSecrets template path for render secrets settings - tplSettingsSecrets base.TplName = "org/settings/secrets" // tplSettingsRunners template path for render runners settings tplSettingsRunners base.TplName = "org/settings/runners" // tplSettingsRunnersEdit template path for render runners edit settings @@ -253,51 +250,3 @@ func Labels(ctx *context.Context) { ctx.Data["LabelTemplates"] = repo_module.LabelTemplates ctx.HTML(http.StatusOK, tplSettingsLabels) } - -// Secrets render organization secrets page -func Secrets(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.secrets") - ctx.Data["PageIsOrgSettings"] = true - ctx.Data["PageIsOrgSettingsSecrets"] = true - - secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID}) - if err != nil { - ctx.ServerError("FindSecrets", err) - return - } - ctx.Data["Secrets"] = secrets - - ctx.HTML(http.StatusOK, tplSettingsSecrets) -} - -// SecretsPost add secrets -func SecretsPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.AddSecretForm) - - _, 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) - 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") -} - -// SecretsDelete delete secrets -func SecretsDelete(ctx *context.Context) { - id := ctx.FormInt64("id") - 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 { - ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) - } - - ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Org.OrgLink + "/settings/secrets", - }) -} diff --git a/routers/web/org/setting_secrets.go b/routers/web/org/setting_secrets.go new file mode 100644 index 000000000000..1cdbe35f32ad --- /dev/null +++ b/routers/web/org/setting_secrets.go @@ -0,0 +1,48 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + shared "code.gitea.io/gitea/routers/web/shared/secrets" +) + +const ( + tplSettingsSecrets base.TplName = "org/settings/secrets" +) + +// Secrets render organization secrets page +func Secrets(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("secrets.secrets") + ctx.Data["PageIsOrgSettings"] = true + ctx.Data["PageIsOrgSettingsSecrets"] = true + + shared.SetSecretsContext(ctx, ctx.ContextUser.ID, 0) + if ctx.Written() { + return + } + + ctx.HTML(http.StatusOK, tplSettingsSecrets) +} + +// SecretsPost add secrets +func SecretsPost(ctx *context.Context) { + shared.PerformSecretsPost( + ctx, + ctx.ContextUser.ID, + 0, + ctx.Org.OrgLink+"/settings/secrets", + ) +} + +// SecretsDelete delete secrets +func SecretsDelete(ctx *context.Context) { + shared.PerformSecretsDelete( + ctx, + ctx.Org.OrgLink+"/settings/secrets", + ) +} diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 73887195e697..e970dab3ebee 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -19,7 +19,6 @@ import ( "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" @@ -1131,33 +1130,9 @@ func DeployKeys(ctx *context.Context) { } ctx.Data["Deploykeys"] = keys - secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID}) - if err != nil { - ctx.ServerError("FindSecrets", err) - return - } - ctx.Data["Secrets"] = secrets - ctx.HTML(http.StatusOK, tplDeployKeys) } -// SecretsPost response for creating a new secret -func SecretsPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.AddSecretForm) - - _, 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) - 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") -} - // DeployKeysPost response for adding a deploy key of a repository func DeployKeysPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.AddKeyForm) @@ -1219,20 +1194,6 @@ func DeployKeysPost(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") } -func DeleteSecret(ctx *context.Context) { - id := ctx.FormInt64("id") - 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 { - ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) - } - - ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/settings/keys", - }) -} - // DeleteDeployKey response for deleting a deploy key func DeleteDeployKey(ctx *context.Context) { if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil { diff --git a/routers/web/repo/setting_secrets.go b/routers/web/repo/setting_secrets.go new file mode 100644 index 000000000000..c42dee583b92 --- /dev/null +++ b/routers/web/repo/setting_secrets.go @@ -0,0 +1,46 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + shared "code.gitea.io/gitea/routers/web/shared/secrets" +) + +const ( + tplSecrets base.TplName = "repo/settings/secrets" +) + +func Secrets(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("secrets.secrets") + ctx.Data["PageIsSettingsSecrets"] = true + ctx.Data["DisableSSH"] = setting.SSH.Disabled + + shared.SetSecretsContext(ctx, 0, ctx.Repo.Repository.ID) + if ctx.Written() { + return + } + + ctx.HTML(http.StatusOK, tplSecrets) +} + +func SecretsPost(ctx *context.Context) { + shared.PerformSecretsPost( + ctx, + 0, + ctx.Repo.Repository.ID, + ctx.Repo.RepoLink+"/settings/secrets", + ) +} + +func DeleteSecret(ctx *context.Context) { + shared.PerformSecretsDelete( + ctx, + ctx.Repo.RepoLink+"/settings/secrets", + ) +} diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go new file mode 100644 index 000000000000..e242c5e81611 --- /dev/null +++ b/routers/web/shared/secrets/secrets.go @@ -0,0 +1,54 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package secrets + +import ( + "net/http" + + "code.gitea.io/gitea/models/db" + secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/forms" +) + +func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { + secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) + if err != nil { + ctx.ServerError("FindSecrets", err) + return + } + + ctx.Data["Secrets"] = secrets +} + +func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { + form := web.GetForm(ctx).(*forms.AddSecretForm) + + s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, form.Title, form.Content) + if err != nil { + log.Error("InsertEncryptedSecret: %v", err) + ctx.Flash.Error(ctx.Tr("secrets.creation.failed")) + } else { + ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name)) + } + + ctx.Redirect(redirectURL) +} + +func PerformSecretsDelete(ctx *context.Context, redirectURL string) { + id := ctx.FormInt64("id") + + if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil { + log.Error("Delete secret %d failed: %v", id, err) + ctx.Flash.Error(ctx.Tr("secrets.deletion.failed")) + } else { + ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "redirect": redirectURL, + }) +} diff --git a/routers/web/user/setting/secrets.go b/routers/web/user/setting/secrets.go new file mode 100644 index 000000000000..3a57897d8f60 --- /dev/null +++ b/routers/web/user/setting/secrets.go @@ -0,0 +1,45 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + shared "code.gitea.io/gitea/routers/web/shared/secrets" +) + +const ( + tplSettingsSecrets base.TplName = "user/settings/secrets" +) + +func Secrets(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("secrets.secrets") + ctx.Data["PageIsSettingsSecrets"] = true + + shared.SetSecretsContext(ctx, ctx.Doer.ID, 0) + if ctx.Written() { + return + } + + ctx.HTML(http.StatusOK, tplSettingsSecrets) +} + +func SecretsPost(ctx *context.Context) { + shared.PerformSecretsPost( + ctx, + ctx.Doer.ID, + 0, + setting.AppSubURL+"/user/settings/secrets", + ) +} + +func SecretsDelete(ctx *context.Context) { + shared.PerformSecretsDelete( + ctx, + setting.AppSubURL+"/user/settings/secrets", + ) +} diff --git a/routers/web/web.go b/routers/web/web.go index 88a12df19da2..b7128fc3a9fc 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -469,6 +469,11 @@ func RegisterRoutes(m *web.Route) { }) }) }, packagesEnabled) + m.Group("/secrets", func() { + m.Get("", user_setting.Secrets) + m.Post("", web.Bind(forms.AddSecretForm{}), user_setting.SecretsPost) + m.Post("/delete", user_setting.SecretsDelete) + }) m.Get("/organization", user_setting.Organization) m.Get("/repos", user_setting.Repos) m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository) @@ -982,10 +987,12 @@ func RegisterRoutes(m *web.Route) { m.Combo("").Get(repo.DeployKeys). Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost) m.Post("/delete", repo.DeleteDeployKey) - m.Group("/secrets", func() { - m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost) - m.Post("/delete", repo.DeleteSecret) - }) + }) + + m.Group("/secrets", func() { + m.Get("", repo.Secrets) + m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost) + m.Post("/delete", repo.DeleteSecret) }) m.Group("/lfs", func() { diff --git a/templates/org/settings/secrets.tmpl b/templates/org/settings/secrets.tmpl index dd2a437b7525..70add15f50fb 100644 --- a/templates/org/settings/secrets.tmpl +++ b/templates/org/settings/secrets.tmpl @@ -6,78 +6,10 @@ {{template "org/settings/navbar" .}}
{{template "base/alert" .}} -

- {{.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}} -
- {{else}} - {{.locale.Tr "secrets.none"}} - {{end}} -
+ {{template "shared/secrets/add_list" .}}
- - {{template "base/footer" .}} diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index 32a1258b3a9a..44c916eefbdb 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -51,7 +51,7 @@ {{range .Deploykeys}}
-
@@ -75,11 +75,9 @@ {{end}}
-
- {{template "repo/settings/secrets" .}} -