Skip to content

Commit

Permalink
feat(API): add route and implementation for creating/updating reposit…
Browse files Browse the repository at this point in the history
…ory secret (#26766)

spec:
https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-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 <[email protected]>
Co-authored-by: Giteabot <[email protected]>
  • Loading branch information
appleboy and GiteaBot authored Aug 29, 2023
1 parent 2d9249b commit b91057b
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 17 deletions.
28 changes: 28 additions & 0 deletions models/secret/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
6 changes: 5 additions & 1 deletion routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -1301,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() {
Expand Down
23 changes: 7 additions & 16 deletions routers/api/v1/org/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -108,26 +108,17 @@ 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)
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
}

Expand Down
75 changes: 75 additions & 0 deletions routers/api/v1/repo/action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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, "CreateOrUpdateSecret", err)
return
}
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
isCreated, err := secret_model.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, secretName, opt.Data)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
return
}
if isCreated {
ctx.Status(http.StatusCreated)
return
}

ctx.Status(http.StatusNoContent)
}
59 changes: 59 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b91057b

Please sign in to comment.