Skip to content

Commit b56057a

Browse files
committed
Add instance-level secrets
1 parent 92711b0 commit b56057a

File tree

14 files changed

+348
-44
lines changed

14 files changed

+348
-44
lines changed

models/secret/secret.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ func init() {
6464
}
6565

6666
func (s *Secret) Validate() error {
67-
if s.OwnerID == 0 && s.RepoID == 0 {
68-
return errors.New("the secret is not bound to any scope")
67+
if s.OwnerID != 0 && s.RepoID != 0 {
68+
return errors.New("a secret should not be bound to an owner and a repository at the same time")
6969
}
7070
return nil
7171
}
@@ -80,12 +80,8 @@ type FindSecretsOptions struct {
8080

8181
func (opts FindSecretsOptions) ToConds() builder.Cond {
8282
cond := builder.NewCond()
83-
if opts.OwnerID > 0 {
84-
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
85-
}
86-
if opts.RepoID > 0 {
87-
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
88-
}
83+
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
84+
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
8985
if opts.SecretID != 0 {
9086
cond = cond.And(builder.Eq{"id": opts.SecretID})
9187
}

routers/api/actions/runner/utils.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,24 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s
6868
return secrets
6969
}
7070

71-
ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID})
71+
globalSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: 0, RepoID: 0})
72+
if err != nil {
73+
log.Error("find global secrets: %v", err)
74+
// go on
75+
}
76+
ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID, RepoID: 0})
7277
if err != nil {
7378
log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err)
7479
// go on
7580
}
76-
repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID})
81+
repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: 0, RepoID: task.Job.Run.RepoID})
7782
if err != nil {
7883
log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err)
7984
// go on
8085
}
8186

82-
for _, secret := range append(ownerSecrets, repoSecrets...) {
87+
// Level precedence: Repo > Org / User > Global
88+
for _, secret := range append(globalSecrets, append(ownerSecrets, repoSecrets...)...) {
8389
if v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data); err != nil {
8490
log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err)
8591
// go on

routers/api/v1/admin/action.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package admin
5+
6+
import (
7+
"errors"
8+
"net/http"
9+
10+
"code.gitea.io/gitea/modules/context"
11+
api "code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/modules/util"
13+
"code.gitea.io/gitea/modules/web"
14+
secret_service "code.gitea.io/gitea/services/secrets"
15+
)
16+
17+
// CreateOrUpdateSecret create or update one secret in instance scope
18+
func CreateOrUpdateSecret(ctx *context.APIContext) {
19+
// swagger:operation PUT /admin/actions/secrets/{secretname} admin updateAdminSecret
20+
// ---
21+
// summary: Create or Update a secret value in instance scope
22+
// consumes:
23+
// - application/json
24+
// produces:
25+
// - application/json
26+
// parameters:
27+
// - name: secretname
28+
// in: path
29+
// description: name of the secret
30+
// type: string
31+
// required: true
32+
// - name: body
33+
// in: body
34+
// schema:
35+
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
36+
// responses:
37+
// "201":
38+
// description: secret created
39+
// "204":
40+
// description: secret updated
41+
// "400":
42+
// "$ref": "#/responses/error"
43+
// "404":
44+
// "$ref": "#/responses/notFound"
45+
46+
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
47+
48+
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, 0, ctx.Params("secretname"), opt.Data)
49+
if err != nil {
50+
if errors.Is(err, util.ErrInvalidArgument) {
51+
ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
52+
} else if errors.Is(err, util.ErrNotExist) {
53+
ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
54+
} else {
55+
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
56+
}
57+
return
58+
}
59+
60+
if created {
61+
ctx.Status(http.StatusCreated)
62+
} else {
63+
ctx.Status(http.StatusNoContent)
64+
}
65+
}
66+
67+
// DeleteSecret delete one secret in instance scope
68+
func DeleteSecret(ctx *context.APIContext) {
69+
// swagger:operation DELETE /admin/actions/secrets/{secretname} admin deleteAdminSecret
70+
// ---
71+
// summary: Delete a secret in instance scope
72+
// consumes:
73+
// - application/json
74+
// produces:
75+
// - application/json
76+
// parameters:
77+
// - name: secretname
78+
// in: path
79+
// description: name of the secret
80+
// type: string
81+
// required: true
82+
// responses:
83+
// "204":
84+
// description: secret deleted
85+
// "400":
86+
// "$ref": "#/responses/error"
87+
// "404":
88+
// "$ref": "#/responses/notFound"
89+
90+
err := secret_service.DeleteSecretByName(ctx, 0, 0, ctx.Params("secretname"))
91+
if err != nil {
92+
if errors.Is(err, util.ErrInvalidArgument) {
93+
ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
94+
} else if errors.Is(err, util.ErrNotExist) {
95+
ctx.Error(http.StatusNotFound, "DeleteSecret", err)
96+
} else {
97+
ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
98+
}
99+
return
100+
}
101+
102+
ctx.Status(http.StatusNoContent)
103+
}

routers/api/v1/api.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,6 @@ func Routes() *web.Route {
948948
Post(bind(api.CreateEmailOption{}), user.AddEmail).
949949
Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail)
950950

951-
// manage user-level actions features
952951
m.Group("/actions", func() {
953952
m.Group("/secrets", func() {
954953
m.Combo("/{secretname}").
@@ -1499,6 +1498,11 @@ func Routes() *web.Route {
14991498
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership())
15001499

15011500
m.Group("/admin", func() {
1501+
m.Group("/actions/secrets", func() {
1502+
m.Combo("/{secretname}").
1503+
Put(bind(api.CreateOrUpdateSecretOption{}), admin.CreateOrUpdateSecret).
1504+
Delete(admin.DeleteSecret)
1505+
})
15021506
m.Group("/cron", func() {
15031507
m.Get("", admin.ListCronTasks)
15041508
m.Post("/{task}", admin.PostCronTask)

routers/api/v1/org/secrets.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func ListActionsSecrets(ctx *context.APIContext) {
6767
ctx.JSON(http.StatusOK, apiSecrets)
6868
}
6969

70-
// create or update one secret of the organization
70+
// CreateOrUpdateSecret create or update one secret in an organization
7171
func CreateOrUpdateSecret(ctx *context.APIContext) {
7272
// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
7373
// ---
@@ -93,9 +93,9 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
9393
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
9494
// responses:
9595
// "201":
96-
// description: response when creating a secret
96+
// description: secret created
9797
// "204":
98-
// description: response when updating a secret
98+
// description: secret updated
9999
// "400":
100100
// "$ref": "#/responses/error"
101101
// "404":
@@ -122,7 +122,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
122122
}
123123
}
124124

125-
// DeleteSecret delete one secret of the organization
125+
// DeleteSecret delete one secret in an organization
126126
func DeleteSecret(ctx *context.APIContext) {
127127
// swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
128128
// ---
@@ -144,7 +144,7 @@ func DeleteSecret(ctx *context.APIContext) {
144144
// required: true
145145
// responses:
146146
// "204":
147-
// description: delete one secret of the organization
147+
// description: secret deleted
148148
// "400":
149149
// "$ref": "#/responses/error"
150150
// "404":

routers/api/v1/repo/action.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
secret_service "code.gitea.io/gitea/services/secrets"
1515
)
1616

17-
// create or update one secret of the repository
17+
// CreateOrUpdateSecret create or update one secret in a repository
1818
func CreateOrUpdateSecret(ctx *context.APIContext) {
1919
// swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret
2020
// ---
@@ -45,20 +45,19 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
4545
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
4646
// responses:
4747
// "201":
48-
// description: response when creating a secret
48+
// description: secret created
4949
// "204":
50-
// description: response when updating a secret
50+
// description: secret updated
5151
// "400":
5252
// "$ref": "#/responses/error"
5353
// "404":
5454
// "$ref": "#/responses/notFound"
5555

56-
owner := ctx.Repo.Owner
5756
repo := ctx.Repo.Repository
5857

5958
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
6059

61-
_, created, err := secret_service.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, ctx.Params("secretname"), opt.Data)
60+
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.Params("secretname"), opt.Data)
6261
if err != nil {
6362
if errors.Is(err, util.ErrInvalidArgument) {
6463
ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
@@ -77,7 +76,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
7776
}
7877
}
7978

80-
// DeleteSecret delete one secret of the repository
79+
// DeleteSecret delete one secret in a repository
8180
func DeleteSecret(ctx *context.APIContext) {
8281
// swagger:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secretname} repository deleteRepoSecret
8382
// ---
@@ -104,16 +103,15 @@ func DeleteSecret(ctx *context.APIContext) {
104103
// required: true
105104
// responses:
106105
// "204":
107-
// description: delete one secret of the organization
106+
// description: secret deleted
108107
// "400":
109108
// "$ref": "#/responses/error"
110109
// "404":
111110
// "$ref": "#/responses/notFound"
112111

113-
owner := ctx.Repo.Owner
114112
repo := ctx.Repo.Repository
115113

116-
err := secret_service.DeleteSecretByName(ctx, owner.ID, repo.ID, ctx.Params("secretname"))
114+
err := secret_service.DeleteSecretByName(ctx, 0, repo.ID, ctx.Params("secretname"))
117115
if err != nil {
118116
if errors.Is(err, util.ErrInvalidArgument) {
119117
ctx.Error(http.StatusBadRequest, "DeleteSecret", err)

routers/api/v1/user/action.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
secret_service "code.gitea.io/gitea/services/secrets"
1515
)
1616

17-
// create or update one secret of the user scope
17+
// CreateOrUpdateSecret create or update one secret in a user scope
1818
func CreateOrUpdateSecret(ctx *context.APIContext) {
1919
// swagger:operation PUT /user/actions/secrets/{secretname} user updateUserSecret
2020
// ---
@@ -35,9 +35,9 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
3535
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
3636
// responses:
3737
// "201":
38-
// description: response when creating a secret
38+
// description: secret created
3939
// "204":
40-
// description: response when updating a secret
40+
// description: secret updated
4141
// "400":
4242
// "$ref": "#/responses/error"
4343
// "404":
@@ -64,7 +64,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
6464
}
6565
}
6666

67-
// DeleteSecret delete one secret of the user scope
67+
// DeleteSecret delete one secret in a user scope
6868
func DeleteSecret(ctx *context.APIContext) {
6969
// swagger:operation DELETE /user/actions/secrets/{secretname} user deleteUserSecret
7070
// ---
@@ -81,7 +81,7 @@ func DeleteSecret(ctx *context.APIContext) {
8181
// required: true
8282
// responses:
8383
// "204":
84-
// description: delete one secret of the user
84+
// description: secret deleted
8585
// "400":
8686
// "$ref": "#/responses/error"
8787
// "404":

routers/web/repo/setting/secrets.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import (
1616

1717
const (
1818
// TODO: Separate secrets from runners when layout is ready
19-
tplRepoSecrets base.TplName = "repo/settings/actions"
20-
tplOrgSecrets base.TplName = "org/settings/actions"
21-
tplUserSecrets base.TplName = "user/settings/actions"
19+
tplRepoSecrets base.TplName = "repo/settings/actions"
20+
tplOrgSecrets base.TplName = "org/settings/actions"
21+
tplUserSecrets base.TplName = "user/settings/actions"
22+
tplAdminSecrets base.TplName = "admin/actions"
2223
)
2324

2425
type secretsCtx struct {
@@ -27,10 +28,12 @@ type secretsCtx struct {
2728
IsRepo bool
2829
IsOrg bool
2930
IsUser bool
31+
IsGlobal bool
3032
SecretsTemplate base.TplName
3133
RedirectLink string
3234
}
3335

36+
//nolint:dupl
3437
func getSecretsCtx(ctx *context.Context) (*secretsCtx, error) {
3538
if ctx.Data["PageIsRepoSettings"] == true {
3639
return &secretsCtx{
@@ -67,6 +70,16 @@ func getSecretsCtx(ctx *context.Context) (*secretsCtx, error) {
6770
}, nil
6871
}
6972

73+
if ctx.Data["PageIsAdmin"] == true {
74+
return &secretsCtx{
75+
OwnerID: 0,
76+
RepoID: 0,
77+
IsGlobal: true,
78+
SecretsTemplate: tplAdminSecrets,
79+
RedirectLink: setting.AppSubURL + "/admin/actions/secrets",
80+
}, nil
81+
}
82+
7083
return nil, errors.New("unable to set Secrets context")
7184
}
7285

routers/web/repo/setting/variables.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type variablesCtx struct {
3232
RedirectLink string
3333
}
3434

35+
//nolint:dupl
3536
func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
3637
if ctx.Data["PageIsRepoSettings"] == true {
3738
return &variablesCtx{

routers/web/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ func registerRoutes(m *web.Route) {
775775
m.Group("/actions", func() {
776776
m.Get("", admin.RedirectToDefaultSetting)
777777
addSettingsRunnersRoutes()
778+
addSettingsSecretsRoutes()
778779
addSettingsVariablesRoutes()
779780
})
780781
}, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enable, "EnablePackages", setting.Packages.Enabled))

templates/admin/actions.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
{{if eq .PageType "runners"}}
44
{{template "shared/actions/runner_list" .}}
55
{{end}}
6+
{{if eq .PageType "secrets"}}
7+
{{template "shared/secrets/add_list" .}}
8+
{{end}}
69
{{if eq .PageType "variables"}}
710
{{template "shared/variables/variable_list" .}}
811
{{end}}

0 commit comments

Comments
 (0)