aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBo-Yi Wu <appleboy.tw@gmail.com>2023-08-24 10:07:00 +0800
committerGitHub <noreply@github.com>2023-08-24 02:07:00 +0000
commitb62c8e7765a371600a300f62da96483a1ae0c731 (patch)
tree77e2f06e361039ba981bc7927ee9c4a1a6aa5af0
parent7e30986667cc0c460a9fb38e32862e492fd4ca8d (diff)
downloadgitea-b62c8e7765a371600a300f62da96483a1ae0c731.tar.gz
gitea-b62c8e7765a371600a300f62da96483a1ae0c731.zip
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>
-rw-r--r--models/secret/secret.go67
-rw-r--r--modules/structs/secret.go9
-rw-r--r--routers/api/v1/api.go3
-rw-r--r--routers/api/v1/org/action.go91
-rw-r--r--routers/api/v1/swagger/options.go3
-rw-r--r--templates/swagger/v1_json.tmpl100
6 files changed, 272 insertions, 1 deletions
diff --git a/models/secret/secret.go b/models/secret/secret.go
index c9c95e82d3..410cb3770e 100644
--- a/models/secret/secret.go
+++ b/models/secret/secret.go
@@ -6,12 +6,14 @@ package secret
import (
"context"
"errors"
+ "fmt"
"strings"
"code.gitea.io/gitea/models/db"
secret_module "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -26,6 +28,25 @@ type Secret struct {
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
+// ErrSecretNotFound represents a "secret not found" error.
+type ErrSecretNotFound struct {
+ Name string
+}
+
+// IsErrSecretNotFound checks if an error is a ErrSecretNotFound.
+func IsErrSecretNotFound(err error) bool {
+ _, ok := err.(ErrSecretNotFound)
+ return ok
+}
+
+func (err ErrSecretNotFound) Error() string {
+ return fmt.Sprintf("secret was not found [name: %s]", err.Name)
+}
+
+func (err ErrSecretNotFound) Unwrap() error {
+ return util.ErrNotExist
+}
+
// newSecret Creates a new already encrypted secret
func newSecret(ownerID, repoID int64, name, data string) *Secret {
return &Secret{
@@ -93,3 +114,49 @@ func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error
func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret))
}
+
+// UpdateSecret changes org or user reop secret.
+func UpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) error {
+ sc := new(Secret)
+ name = strings.ToUpper(name)
+ has, err := db.GetEngine(ctx).
+ Where("owner_id=?", orgID).
+ And("repo_id=?", repoID).
+ And("name=?", name).
+ Get(sc)
+ if err != nil {
+ return err
+ } else if !has {
+ return ErrSecretNotFound{Name: name}
+ }
+
+ encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
+ if err != nil {
+ return err
+ }
+
+ sc.Data = encrypted
+ _, err = db.GetEngine(ctx).ID(sc.ID).Cols("data").Update(sc)
+ return err
+}
+
+// DeleteSecret deletes secret from an organization.
+func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error {
+ sc := new(Secret)
+ has, err := db.GetEngine(ctx).
+ Where("owner_id=?", orgID).
+ And("repo_id=?", repoID).
+ And("name=?", strings.ToUpper(name)).
+ Get(sc)
+ if err != nil {
+ return err
+ } else if !has {
+ return ErrSecretNotFound{Name: name}
+ }
+
+ if _, err := db.GetEngine(ctx).ID(sc.ID).Delete(new(Secret)); err != nil {
+ return fmt.Errorf("Delete: %w", err)
+ }
+
+ return nil
+}
diff --git a/modules/structs/secret.go b/modules/structs/secret.go
index c707eb2278..52221b51f0 100644
--- a/modules/structs/secret.go
+++ b/modules/structs/secret.go
@@ -25,3 +25,12 @@ type CreateSecretOption struct {
// Data of the secret to create
Data string `json:"data" binding:"Required"`
}
+
+// UpdateSecretOption options when updating secret
+// swagger:model
+type UpdateSecretOption struct {
+ // Data of the secret to update
+ //
+ // required: true
+ Data string `json:"data" binding:"Required"`
+}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 9613bd610d..2d644507d5 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1301,6 +1301,9 @@ func Routes() *web.Route {
m.Group("/actions/secrets", func() {
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateSecretOption{}), org.CreateOrgSecret)
+ m.Combo("/{secretname}").
+ Put(reqToken(), reqOrgOwnership(), bind(api.UpdateSecretOption{}), org.UpdateOrgSecret).
+ Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret)
})
m.Group("/public_members", func() {
m.Get("", org.ListPublicMembers)
diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go
index 7659191946..9697a11363 100644
--- a/routers/api/v1/org/action.go
+++ b/routers/api/v1/org/action.go
@@ -103,6 +103,10 @@ func CreateOrgSecret(ctx *context.APIContext) {
// "403":
// "$ref": "#/responses/forbidden"
opt := web.GetForm(ctx).(*api.CreateSecretOption)
+ if err := actions.NameRegexMatch(opt.Name); err != nil {
+ ctx.Error(http.StatusBadRequest, "CreateOrgSecret", err)
+ return
+ }
s, err := secret_model.InsertEncryptedSecret(
ctx, ctx.Org.Organization.ID, 0, opt.Name, actions.ReserveLineBreakForTextarea(opt.Data),
)
@@ -113,3 +117,90 @@ func CreateOrgSecret(ctx *context.APIContext) {
ctx.JSON(http.StatusCreated, convert.ToSecret(s))
}
+
+// UpdateOrgSecret update one secret of the organization
+func UpdateOrgSecret(ctx *context.APIContext) {
+ // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
+ // ---
+ // summary: Update a secret value in an organization
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of organization
+ // type: string
+ // required: true
+ // - name: secretname
+ // in: path
+ // description: name of the secret
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/UpdateSecretOption"
+ // responses:
+ // "204":
+ // description: update one secret of the organization
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ secretName := ctx.Params(":secretname")
+ opt := web.GetForm(ctx).(*api.UpdateSecretOption)
+ err := secret_model.UpdateSecret(
+ ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data,
+ )
+ if secret_model.IsErrSecretNotFound(err) {
+ ctx.NotFound(err)
+ return
+ }
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "UpdateSecret", err)
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// DeleteOrgSecret delete one secret of the organization
+func DeleteOrgSecret(ctx *context.APIContext) {
+ // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
+ // ---
+ // summary: Delete a secret in an organization
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of organization
+ // type: string
+ // required: true
+ // - name: secretname
+ // in: path
+ // description: name of the secret
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: delete one secret of the organization
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ secretName := ctx.Params(":secretname")
+ err := secret_model.DeleteSecret(
+ ctx, ctx.Org.Organization.ID, 0, secretName,
+ )
+ if secret_model.IsErrSecretNotFound(err) {
+ ctx.NotFound(err)
+ return
+ }
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 8e7e6ec3df..e41ee66776 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -190,4 +190,7 @@ type swaggerParameterBodies struct {
// in:body
CreateSecretOption api.CreateSecretOption
+
+ // in:body
+ UpdateSecretOption api.UpdateSecretOption
}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 5e75f6f8b4..aff4490899 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -1631,6 +1631,89 @@
}
}
},
+ "/orgs/{org}/actions/secrets/{secretname}": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Update a secret value in an organization",
+ "operationId": "updateOrgSecret",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the secret",
+ "name": "secretname",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/UpdateSecretOption"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "update one secret of the organization"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ }
+ }
+ },
+ "delete": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Delete a secret in an organization",
+ "operationId": "deleteOrgSecret",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the secret",
+ "name": "secretname",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "delete one secret of the organization"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ }
+ }
+ }
+ },
"/orgs/{org}/activities/feeds": {
"get": {
"produces": [
@@ -21891,6 +21974,21 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "UpdateSecretOption": {
+ "description": "UpdateSecretOption options when updating secret",
+ "type": "object",
+ "required": [
+ "data"
+ ],
+ "properties": {
+ "data": {
+ "description": "Data of the secret to update",
+ "type": "string",
+ "x-go-name": "Data"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"UpdateUserAvatarOption": {
"description": "UpdateUserAvatarUserOption options when updating the user avatar",
"type": "object",
@@ -23207,7 +23305,7 @@
"parameterBodies": {
"description": "parameterBodies",
"schema": {
- "$ref": "#/definitions/CreateSecretOption"
+ "$ref": "#/definitions/UpdateSecretOption"
}
},
"redirect": {