Skip to content
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

Add user secrets #22191

Merged
merged 7 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
16 changes: 8 additions & 8 deletions docs/content/doc/secrets/overview.en-us.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
date: "2022-12-19T21:26:00+08:00"
title: "Encrypted secrets"
title: "Secrets"
lunny marked this conversation as resolved.
Show resolved Hide resolved
slug: "secrets/overview"
draft: false
toc: false
Expand All @@ -12,24 +12,24 @@ menu:
identifier: "overview"
---

# Encrypted secrets
# Secrets

Encrypted secrets allow you to store sensitive information in your organization or repository.
Secrets allow you to store sensitive information in your user, organization or repository.
Secrets are available on Gitea 1.19+.

# Naming your secrets

The following rules apply to secret names:

Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
- Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.

Secret names must not start with the `GITHUB_` and `GITEA_` prefix.
- Secret names must not start with the `GITHUB_` and `GITEA_` prefix.

Secret names must not start with a number.
- Secret names must not start with a number.

Secret names are not case-sensitive.
- Secret names are not case-sensitive.

Secret names must be unique at the level they are created at.
- Secret names must be unique at the level they are created at.

For example, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level.

Expand Down
2 changes: 1 addition & 1 deletion options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3225,6 +3225,6 @@ creation.value_placeholder = Input any content. Whitespace at the start and end
creation.success = The secret '%s' has been added.
creation.failed = Failed to add secret.
deletion = Remove secret
deletion.description = Removing a secret will revoke its access to repositories. Continue?
deletion.description = Removing a secret is permanent and cannot be undone. Continue?
deletion.success = The secret has been removed.
deletion.failed = Failed to remove secret.
51 changes: 0 additions & 51 deletions routers/web/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
secret_model "code.gitea.io/gitea/models/secret"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
Expand All @@ -38,8 +37,6 @@ const (
tplSettingsHooks base.TplName = "org/settings/hooks"
// tplSettingsLabels template path for render labels settings
tplSettingsLabels base.TplName = "org/settings/labels"
// tplSettingsSecrets template path for render secrets settings
tplSettingsSecrets base.TplName = "org/settings/secrets"
)

// Settings render the main settings page
Expand Down Expand Up @@ -249,51 +246,3 @@ func Labels(ctx *context.Context) {
ctx.Data["LabelTemplates"] = repo_module.LabelTemplates
ctx.HTML(http.StatusOK, tplSettingsLabels)
}

// Secrets render organization secrets page
func Secrets(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.secrets")
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsOrgSettingsSecrets"] = true

secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID})
if err != nil {
ctx.ServerError("FindSecrets", err)
return
}
ctx.Data["Secrets"] = secrets

ctx.HTML(http.StatusOK, tplSettingsSecrets)
}

// SecretsPost add secrets
func SecretsPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.AddSecretForm)

_, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, form.Content)
if err != nil {
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
log.Error("validate secret: %v", err)
ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
return
}

log.Trace("Org %d: secret added", ctx.Org.Organization.ID)
ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title))
ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
}

// SecretsDelete delete secrets
func SecretsDelete(ctx *context.Context) {
id := ctx.FormInt64("id")
if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
log.Error("delete secret %d: %v", id, err)
} else {
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": ctx.Org.OrgLink + "/settings/secrets",
})
}
48 changes: 48 additions & 0 deletions routers/web/org/setting_secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package org

import (
"net/http"

"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
shared "code.gitea.io/gitea/routers/web/shared/secrets"
)

const (
tplSettingsSecrets base.TplName = "org/settings/secrets"
)

// Secrets render organization secrets page
func Secrets(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsOrgSettingsSecrets"] = true

shared.SetSecretsContext(ctx, ctx.ContextUser.ID, 0)
if ctx.Written() {
return
}

ctx.HTML(http.StatusOK, tplSettingsSecrets)
}

// SecretsPost add secrets
func SecretsPost(ctx *context.Context) {
shared.PerformSecretsPost(
ctx,
ctx.ContextUser.ID,
0,
ctx.Org.OrgLink+"/settings/secrets",
)
}

// SecretsDelete delete secrets
func SecretsDelete(ctx *context.Context) {
shared.PerformSecretsDelete(
ctx,
ctx.Org.OrgLink+"/settings/secrets",
)
}
39 changes: 3 additions & 36 deletions routers/web/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
secret_model "code.gitea.io/gitea/models/secret"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
Expand All @@ -38,6 +37,7 @@ import (
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
shared "code.gitea.io/gitea/routers/web/shared/secrets"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
Expand Down Expand Up @@ -1114,33 +1114,14 @@ func DeployKeys(ctx *context.Context) {
}
ctx.Data["Deploykeys"] = keys

secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil {
ctx.ServerError("FindSecrets", err)
shared.SetSecretsContext(ctx, 0, ctx.Repo.Repository.ID)
if ctx.Written() {
return
}
ctx.Data["Secrets"] = secrets

ctx.HTML(http.StatusOK, tplDeployKeys)
}

// SecretsPost response for creating a new secret
func SecretsPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.AddSecretForm)

_, err := secret_model.InsertEncryptedSecret(ctx, 0, ctx.Repo.Repository.ID, form.Title, form.Content)
if err != nil {
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
log.Error("validate secret: %v", err)
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
return
}

log.Trace("Secret added: %d", ctx.Repo.Repository.ID)
ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
}

// DeployKeysPost response for adding a deploy key of a repository
func DeployKeysPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.AddKeyForm)
Expand Down Expand Up @@ -1203,20 +1184,6 @@ func DeployKeysPost(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
}

func DeleteSecret(ctx *context.Context) {
id := ctx.FormInt64("id")
if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
log.Error("delete secret %d: %v", id, err)
} else {
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": ctx.Repo.RepoLink + "/settings/keys",
})
}

// DeleteDeployKey response for deleting a deploy key
func DeleteDeployKey(ctx *context.Context) {
if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
Expand Down
46 changes: 46 additions & 0 deletions routers/web/repo/setting_secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
"net/http"

"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared "code.gitea.io/gitea/routers/web/shared/secrets"
)

const (
tplSecrets base.TplName = "repo/settings/secrets"
)

func Secrets(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
ctx.Data["PageIsSettingsSecrets"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled
Copy link
Member

@delvh delvh Dec 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why DisableSSH here?


shared.SetSecretsContext(ctx, 0, ctx.Repo.Repository.ID)
if ctx.Written() {
return
}

ctx.HTML(http.StatusOK, tplSecrets)
}

func SecretsPost(ctx *context.Context) {
shared.PerformSecretsPost(
ctx,
0,
ctx.Repo.Repository.ID,
ctx.Repo.RepoLink+"/settings/secrets",
)
}

func DeleteSecret(ctx *context.Context) {
shared.PerformSecretsDelete(
ctx,
ctx.Repo.RepoLink+"/settings/secrets",
)
}
54 changes: 54 additions & 0 deletions routers/web/shared/secrets/secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package secrets

import (
"net/http"

"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
)

func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetFoundSecrets?

secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID})
if err != nil {
ctx.ServerError("FindSecrets", err)
return
}

ctx.Data["Secrets"] = secrets
}

func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateSecret/CreateSecretFromCtx?

form := web.GetForm(ctx).(*forms.AddSecretForm)

s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, form.Title, form.Content)
if err != nil {
log.Error("InsertEncryptedSecret: %v", err)
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
} else {
ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name))
}

ctx.Redirect(redirectURL)
}

func PerformSecretsDelete(ctx *context.Context, redirectURL string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeleteSecret?

id := ctx.FormInt64("id")

if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
log.Error("Delete secret %d failed: %v", id, err)
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
} else {
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": redirectURL,
})
}
45 changes: 45 additions & 0 deletions routers/web/user/setting/secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package setting

import (
"net/http"

"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared "code.gitea.io/gitea/routers/web/shared/secrets"
)

const (
tplSettingsSecrets base.TplName = "user/settings/secrets"
)

func Secrets(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
ctx.Data["PageIsSettingsSecrets"] = true

shared.SetSecretsContext(ctx, ctx.Doer.ID, 0)
if ctx.Written() {
return
}

ctx.HTML(http.StatusOK, tplSettingsSecrets)
}

func SecretsPost(ctx *context.Context) {
shared.PerformSecretsPost(
ctx,
ctx.Doer.ID,
0,
setting.AppSubURL+"/user/settings/secrets",
)
}

func SecretsDelete(ctx *context.Context) {
shared.PerformSecretsDelete(
ctx,
setting.AppSubURL+"/user/settings/secrets",
)
}
Loading