Skip to content

feat(API): update and delete secret for managing organization secrets #26660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8034027
feat: add functions and structs for managing organization secrets
appleboy Aug 22, 2023
676b59c
docs: update comments for OrgSecret functions
appleboy Aug 22, 2023
94eba30
Merge branch 'main' of https://github.com/go-gitea/gitea into orgsecret
appleboy Aug 22, 2023
b93811d
docs: update summary comments and descriptions in `v1_json.tmpl` temp…
appleboy Aug 23, 2023
d57d49d
Merge branch 'main' of https://github.com/go-gitea/gitea into orgsecret
appleboy Aug 23, 2023
14b25cd
refactor: update function name and call in secret-related files
appleboy Aug 23, 2023
6d56e35
Merge branch 'main' of https://github.com/go-gitea/gitea into orgsecret
appleboy Aug 23, 2023
da61045
The best label for this code change would be "refactor". This is beca…
appleboy Aug 23, 2023
3a4cd0c
fix: add validation for `Name` field in `CreateOrgSecret` function
appleboy Aug 23, 2023
c40d7e7
Merge branch 'main' of https://github.com/go-gitea/gitea into orgsecret
appleboy Aug 23, 2023
67c0785
Merge branch 'main' of https://github.com/go-gitea/gitea into orgsecret
appleboy Aug 23, 2023
92d59b3
Merge branch 'main' into orgsecret
GiteaBot Aug 23, 2023
249646b
Merge branch 'main' into orgsecret
GiteaBot Aug 23, 2023
6cedfb7
Merge branch 'main' into orgsecret
GiteaBot Aug 23, 2023
e21b3ae
Merge branch 'main' into orgsecret
GiteaBot Aug 23, 2023
4fd741f
Merge branch 'main' into orgsecret
GiteaBot Aug 23, 2023
20c0baa
Merge branch 'main' into orgsecret
GiteaBot Aug 24, 2023
5893302
Merge branch 'main' into orgsecret
GiteaBot Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions models/secret/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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{
Expand Down Expand Up @@ -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
}
9 changes: 9 additions & 0 deletions modules/structs/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
3 changes: 3 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
91 changes: 91 additions & 0 deletions routers/api/v1/org/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
Expand All @@ -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)
}
3 changes: 3 additions & 0 deletions routers/api/v1/swagger/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,7 @@ type swaggerParameterBodies struct {

// in:body
CreateSecretOption api.CreateSecretOption

// in:body
UpdateSecretOption api.UpdateSecretOption
}
100 changes: 99 additions & 1 deletion templates/swagger/v1_json.tmpl

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