From 5cfbcf40fdc77cc34223eda0f9d8106fb0de654e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Mon, 28 Aug 2023 14:49:36 +0800 Subject: [PATCH 1/5] feat: add route and implementation for creating/updating repository secret - Add a new route for creating or updating a secret value in a repository - Create a new file `routers/api/v1/repo/action.go` with the implementation of the `CreateOrUpdateSecret` function - Update the Swagger documentation for the `updateRepoSecret` operation in the `v1_json.tmpl` template file Signed-off-by: Bo-Yi Wu --- routers/api/v1/api.go | 4 ++ routers/api/v1/repo/action.go | 84 ++++++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 61 +++++++++++++++++++++++- 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 routers/api/v1/repo/action.go diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 6424931a47342..93f6d763cf2a9 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -933,6 +933,10 @@ func Routes() *web.Route { m.Post("/accept", repo.AcceptTransfer) m.Post("/reject", repo.RejectTransfer) }, reqToken()) + m.Group("/actions/secrets", func() { + m.Combo("/{secretname}"). + Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret) + }) m.Group("/hooks/git", func() { m.Combo("").Get(repo.ListGitHooks) m.Group("/{id}", func() { diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go new file mode 100644 index 0000000000000..686fed9504bb4 --- /dev/null +++ b/routers/api/v1/repo/action.go @@ -0,0 +1,84 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/context" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/web/shared/actions" +) + +// create or update one secret of the repository +func CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret + // --- + // summary: Create or Update a secret value in a repository + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repository + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateOrUpdateSecretOption" + // responses: + // "201": + // description: response when creating a secret + // "204": + // description: response when updating a secret + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + owner := ctx.Repo.Owner + repo := ctx.Repo.Repository + + secretName := ctx.Params(":secretname") + if err := actions.NameRegexMatch(secretName); err != nil { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateOrgSecret", err) + return + } + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + err := secret_model.UpdateSecret( + ctx, owner.ID, repo.ID, secretName, opt.Data, + ) + if secret_model.IsErrSecretNotFound(err) { + _, err := secret_model.InsertEncryptedSecret( + ctx, owner.ID, repo.ID, secretName, actions.ReserveLineBreakForTextarea(opt.Data), + ) + if err != nil { + ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err) + return + } + ctx.Status(http.StatusCreated) + return + } + if err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateSecret", err) + return + } + + ctx.Status(http.StatusNoContent) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d37f4463f55ca..86880a0f94402 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3230,6 +3230,65 @@ } } }, + "/repos/{owner}/{repo}/actions/secrets/{secretname}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create or Update a secret value in a repository", + "operationId": "updateRepoSecret", + "parameters": [ + { + "type": "string", + "description": "owner of the repository", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repository", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the secret", + "name": "secretname", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreateOrUpdateSecretOption" + } + } + ], + "responses": { + "201": { + "description": "response when creating a secret" + }, + "204": { + "description": "response when updating a secret" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, "/repos/{owner}/{repo}/activities/feeds": { "get": { "produces": [ @@ -23337,4 +23396,4 @@ "TOTPHeader": [] } ] -} +} \ No newline at end of file From 52735c7107a13da2576d6f70bd93292c54f16851 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 29 Aug 2023 17:04:47 +0800 Subject: [PATCH 2/5] feat: refactor secret creation and update functionality - Add a new function `CreateOrUpdateSecret` in `models/secret/secret.go` - Fix a typo in the function name `CreateOrUpdateOrgSecret` to `CreateOrUpdateSecret` in `routers/api/v1/repo/action.go` Signed-off-by: Bo-Yi Wu --- models/secret/secret.go | 28 ++++++++++++++++++++++++++++ routers/api/v1/repo/action.go | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/models/secret/secret.go b/models/secret/secret.go index 410cb3770ebdf..1cb816e9db273 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -160,3 +160,31 @@ func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error { return nil } + +// CreateOrUpdateSecret creates or updates a secret and returns true if it was created +func CreateOrUpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) (bool, 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 false, err + } + + if !has { + _, err = InsertEncryptedSecret(ctx, orgID, repoID, name, data) + if err != nil { + return false, err + } + return true, nil + } + + if err := UpdateSecret(ctx, orgID, repoID, name, data); err != nil { + return false, err + } + + return false, nil +} diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 686fed9504bb4..90c5961c2525d 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -57,7 +57,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) { secretName := ctx.Params(":secretname") if err := actions.NameRegexMatch(secretName); err != nil { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateOrgSecret", err) + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) return } opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) From 62bede0bf0157c2f0b20e17a45076468c32ea40e Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 29 Aug 2023 17:08:39 +0800 Subject: [PATCH 3/5] refactor: refactor secret creation and update logic - Modify the function `CreateOrUpdateOrgSecret` in `routers/api/v1/org/action.go` - Replace the call to `secret_model.UpdateSecret` with `secret_model.CreateOrUpdateSecret` - Remove the block of code that handles the case when the secret is not found - Remove the call to `secret_model.InsertEncryptedSecret` - Add a new variable `isCreated` to check if the secret was created or updated - Update the error handling for the function `CreateOrUpdateOrgSecret` - Modify the function `CreateOrUpdateSecret` in `routers/api/v1/repo/action.go` - Replace the call to `secret_model.UpdateSecret` with `secret_model.CreateOrUpdateSecret` - Remove the block of code that handles the case when the secret is not found - Remove the call to `secret_model.InsertEncryptedSecret` - Add a new variable `isCreated` to check if the secret was created or updated - Update the error handling for the function `CreateOrUpdateSecret` Signed-off-by: Bo-Yi Wu --- routers/api/v1/org/action.go | 19 +++++-------------- routers/api/v1/repo/action.go | 19 +++++-------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index ee18cca26de73..6507037b2e2fc 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -112,22 +112,13 @@ func CreateOrUpdateOrgSecret(ctx *context.APIContext) { return } opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - err := secret_model.UpdateSecret( - ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data, - ) - if secret_model.IsErrSecretNotFound(err) { - _, err := secret_model.InsertEncryptedSecret( - ctx, ctx.Org.Organization.ID, 0, secretName, actions.ReserveLineBreakForTextarea(opt.Data), - ) - if err != nil { - ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err) - return - } - ctx.Status(http.StatusCreated) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) return } - if err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateSecret", err) + if isCreated { + ctx.Status(http.StatusCreated) return } diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 90c5961c2525d..015c731a75d22 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -61,22 +61,13 @@ func CreateOrUpdateSecret(ctx *context.APIContext) { return } opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - err := secret_model.UpdateSecret( - ctx, owner.ID, repo.ID, secretName, opt.Data, - ) - if secret_model.IsErrSecretNotFound(err) { - _, err := secret_model.InsertEncryptedSecret( - ctx, owner.ID, repo.ID, secretName, actions.ReserveLineBreakForTextarea(opt.Data), - ) - if err != nil { - ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err) - return - } - ctx.Status(http.StatusCreated) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, secretName, opt.Data) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) return } - if err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateSecret", err) + if isCreated { + ctx.Status(http.StatusCreated) return } From 8438a0c35c46f79b1978bfbcac636a93a956035c Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 29 Aug 2023 17:09:40 +0800 Subject: [PATCH 4/5] refactor: refactor function names in API and org package - Change the `CreateOrUpdateOrgSecret` function name to `CreateOrUpdateSecret` in `routers/api/v1/api.go` - Change the `CreateOrUpdateOrgSecret` function name to `CreateOrUpdateSecret` in `routers/api/v1/org/action.go` Signed-off-by: Bo-Yi Wu --- routers/api/v1/api.go | 2 +- routers/api/v1/org/action.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 93f6d763cf2a9..32e5a10bbe78d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1305,7 +1305,7 @@ func Routes() *web.Route { m.Group("/actions/secrets", func() { m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) m.Combo("/{secretname}"). - Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateOrgSecret). + Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret). Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret) }) m.Group("/public_members", func() { diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 6507037b2e2fc..0bf741e8256db 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -74,7 +74,7 @@ func listActionsSecrets(ctx *context.APIContext) { } // create or update one secret of the organization -func CreateOrUpdateOrgSecret(ctx *context.APIContext) { +func CreateOrUpdateSecret(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret // --- // summary: Create or Update a secret value in an organization @@ -108,7 +108,7 @@ func CreateOrUpdateOrgSecret(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" secretName := ctx.Params(":secretname") if err := actions.NameRegexMatch(secretName); err != nil { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateOrgSecret", err) + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) return } opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) From 1907d720724550ba76dde6199ecff9d72390c3f6 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 29 Aug 2023 21:44:00 +0800 Subject: [PATCH 5/5] update Signed-off-by: Bo-Yi Wu --- templates/swagger/v1_json.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 86880a0f94402..78491de2e1028 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -23396,4 +23396,4 @@ "TOTPHeader": [] } ] -} \ No newline at end of file +}