Skip to content

Commit b62c8e7

Browse files
authored
feat(API): update and delete secret for managing organization secrets (#26660)
- Add `UpdateSecret` function to modify org or user repo secret - Add `DeleteSecret` function to delete secret from an organization - Add `UpdateSecretOption` struct for updating secret options - Add `UpdateOrgSecret` function to update a secret in an organization - Add `DeleteOrgSecret` function to delete a secret in an organization GitHub API 1. Update Org Secret: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret 2. Delete Org Secret: https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#delete-an-organization-secret --------- Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
1 parent 7e30986 commit b62c8e7

File tree

6 files changed

+272
-1
lines changed

6 files changed

+272
-1
lines changed

models/secret/secret.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ package secret
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"strings"
1011

1112
"code.gitea.io/gitea/models/db"
1213
secret_module "code.gitea.io/gitea/modules/secret"
1314
"code.gitea.io/gitea/modules/setting"
1415
"code.gitea.io/gitea/modules/timeutil"
16+
"code.gitea.io/gitea/modules/util"
1517

1618
"xorm.io/builder"
1719
)
@@ -26,6 +28,25 @@ type Secret struct {
2628
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
2729
}
2830

31+
// ErrSecretNotFound represents a "secret not found" error.
32+
type ErrSecretNotFound struct {
33+
Name string
34+
}
35+
36+
// IsErrSecretNotFound checks if an error is a ErrSecretNotFound.
37+
func IsErrSecretNotFound(err error) bool {
38+
_, ok := err.(ErrSecretNotFound)
39+
return ok
40+
}
41+
42+
func (err ErrSecretNotFound) Error() string {
43+
return fmt.Sprintf("secret was not found [name: %s]", err.Name)
44+
}
45+
46+
func (err ErrSecretNotFound) Unwrap() error {
47+
return util.ErrNotExist
48+
}
49+
2950
// newSecret Creates a new already encrypted secret
3051
func newSecret(ownerID, repoID int64, name, data string) *Secret {
3152
return &Secret{
@@ -93,3 +114,49 @@ func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error
93114
func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) {
94115
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret))
95116
}
117+
118+
// UpdateSecret changes org or user reop secret.
119+
func UpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) error {
120+
sc := new(Secret)
121+
name = strings.ToUpper(name)
122+
has, err := db.GetEngine(ctx).
123+
Where("owner_id=?", orgID).
124+
And("repo_id=?", repoID).
125+
And("name=?", name).
126+
Get(sc)
127+
if err != nil {
128+
return err
129+
} else if !has {
130+
return ErrSecretNotFound{Name: name}
131+
}
132+
133+
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
134+
if err != nil {
135+
return err
136+
}
137+
138+
sc.Data = encrypted
139+
_, err = db.GetEngine(ctx).ID(sc.ID).Cols("data").Update(sc)
140+
return err
141+
}
142+
143+
// DeleteSecret deletes secret from an organization.
144+
func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error {
145+
sc := new(Secret)
146+
has, err := db.GetEngine(ctx).
147+
Where("owner_id=?", orgID).
148+
And("repo_id=?", repoID).
149+
And("name=?", strings.ToUpper(name)).
150+
Get(sc)
151+
if err != nil {
152+
return err
153+
} else if !has {
154+
return ErrSecretNotFound{Name: name}
155+
}
156+
157+
if _, err := db.GetEngine(ctx).ID(sc.ID).Delete(new(Secret)); err != nil {
158+
return fmt.Errorf("Delete: %w", err)
159+
}
160+
161+
return nil
162+
}

modules/structs/secret.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,12 @@ type CreateSecretOption struct {
2525
// Data of the secret to create
2626
Data string `json:"data" binding:"Required"`
2727
}
28+
29+
// UpdateSecretOption options when updating secret
30+
// swagger:model
31+
type UpdateSecretOption struct {
32+
// Data of the secret to update
33+
//
34+
// required: true
35+
Data string `json:"data" binding:"Required"`
36+
}

routers/api/v1/api.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,9 @@ func Routes() *web.Route {
13011301
m.Group("/actions/secrets", func() {
13021302
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
13031303
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateSecretOption{}), org.CreateOrgSecret)
1304+
m.Combo("/{secretname}").
1305+
Put(reqToken(), reqOrgOwnership(), bind(api.UpdateSecretOption{}), org.UpdateOrgSecret).
1306+
Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret)
13041307
})
13051308
m.Group("/public_members", func() {
13061309
m.Get("", org.ListPublicMembers)

routers/api/v1/org/action.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ func CreateOrgSecret(ctx *context.APIContext) {
103103
// "403":
104104
// "$ref": "#/responses/forbidden"
105105
opt := web.GetForm(ctx).(*api.CreateSecretOption)
106+
if err := actions.NameRegexMatch(opt.Name); err != nil {
107+
ctx.Error(http.StatusBadRequest, "CreateOrgSecret", err)
108+
return
109+
}
106110
s, err := secret_model.InsertEncryptedSecret(
107111
ctx, ctx.Org.Organization.ID, 0, opt.Name, actions.ReserveLineBreakForTextarea(opt.Data),
108112
)
@@ -113,3 +117,90 @@ func CreateOrgSecret(ctx *context.APIContext) {
113117

114118
ctx.JSON(http.StatusCreated, convert.ToSecret(s))
115119
}
120+
121+
// UpdateOrgSecret update one secret of the organization
122+
func UpdateOrgSecret(ctx *context.APIContext) {
123+
// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
124+
// ---
125+
// summary: Update a secret value in an organization
126+
// consumes:
127+
// - application/json
128+
// produces:
129+
// - application/json
130+
// parameters:
131+
// - name: org
132+
// in: path
133+
// description: name of organization
134+
// type: string
135+
// required: true
136+
// - name: secretname
137+
// in: path
138+
// description: name of the secret
139+
// type: string
140+
// required: true
141+
// - name: body
142+
// in: body
143+
// schema:
144+
// "$ref": "#/definitions/UpdateSecretOption"
145+
// responses:
146+
// "204":
147+
// description: update one secret of the organization
148+
// "403":
149+
// "$ref": "#/responses/forbidden"
150+
secretName := ctx.Params(":secretname")
151+
opt := web.GetForm(ctx).(*api.UpdateSecretOption)
152+
err := secret_model.UpdateSecret(
153+
ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data,
154+
)
155+
if secret_model.IsErrSecretNotFound(err) {
156+
ctx.NotFound(err)
157+
return
158+
}
159+
if err != nil {
160+
ctx.Error(http.StatusInternalServerError, "UpdateSecret", err)
161+
return
162+
}
163+
164+
ctx.Status(http.StatusNoContent)
165+
}
166+
167+
// DeleteOrgSecret delete one secret of the organization
168+
func DeleteOrgSecret(ctx *context.APIContext) {
169+
// swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
170+
// ---
171+
// summary: Delete a secret in an organization
172+
// consumes:
173+
// - application/json
174+
// produces:
175+
// - application/json
176+
// parameters:
177+
// - name: org
178+
// in: path
179+
// description: name of organization
180+
// type: string
181+
// required: true
182+
// - name: secretname
183+
// in: path
184+
// description: name of the secret
185+
// type: string
186+
// required: true
187+
// responses:
188+
// "204":
189+
// description: delete one secret of the organization
190+
// "403":
191+
// "$ref": "#/responses/forbidden"
192+
secretName := ctx.Params(":secretname")
193+
err := secret_model.DeleteSecret(
194+
ctx, ctx.Org.Organization.ID, 0, secretName,
195+
)
196+
if secret_model.IsErrSecretNotFound(err) {
197+
ctx.NotFound(err)
198+
return
199+
}
200+
if err != nil {
201+
ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
202+
return
203+
}
204+
205+
ctx.Status(http.StatusNoContent)
206+
}

routers/api/v1/swagger/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,7 @@ type swaggerParameterBodies struct {
190190

191191
// in:body
192192
CreateSecretOption api.CreateSecretOption
193+
194+
// in:body
195+
UpdateSecretOption api.UpdateSecretOption
193196
}

templates/swagger/v1_json.tmpl

Lines changed: 99 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)