mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add user secrets (#22191)
Fixes #22183 Replaces #22187 This PR adds secrets for users. I refactored the files for organizations and repos to use the same logic and templates. I splitted the secrets from deploy keys again and reverted the fix from #22187. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
			
		||||
---
 | 
			
		||||
date: "2022-12-19T21:26:00+08:00"
 | 
			
		||||
title: "Encrypted secrets"
 | 
			
		||||
title: "Secrets"
 | 
			
		||||
slug: "secrets/overview"
 | 
			
		||||
draft: false
 | 
			
		||||
toc: false
 | 
			
		||||
@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3254,7 +3254,7 @@ 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -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"
 | 
			
		||||
	// tplSettingsRunners template path for render runners settings
 | 
			
		||||
	tplSettingsRunners base.TplName = "org/settings/runners"
 | 
			
		||||
	// tplSettingsRunnersEdit template path for render runners edit settings
 | 
			
		||||
@@ -253,51 +250,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
									
								
								routers/web/org/setting_secrets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								routers/web/org/setting_secrets.go
									
									
									
									
									
										Normal file
									
								
							@@ -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",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -1131,33 +1130,9 @@ 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)
 | 
			
		||||
		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)
 | 
			
		||||
@@ -1219,20 +1194,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 {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								routers/web/repo/setting_secrets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								routers/web/repo/setting_secrets.go
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
 | 
			
		||||
	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
									
								
								routers/web/shared/secrets/secrets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								routers/web/shared/secrets/secrets.go
									
									
									
									
									
										Normal file
									
								
							@@ -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) {
 | 
			
		||||
	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) {
 | 
			
		||||
	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) {
 | 
			
		||||
	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
									
								
								routers/web/user/setting/secrets.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								routers/web/user/setting/secrets.go
									
									
									
									
									
										Normal file
									
								
							@@ -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",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
@@ -469,6 +469,11 @@ func RegisterRoutes(m *web.Route) {
 | 
			
		||||
				})
 | 
			
		||||
			})
 | 
			
		||||
		}, packagesEnabled)
 | 
			
		||||
		m.Group("/secrets", func() {
 | 
			
		||||
			m.Get("", user_setting.Secrets)
 | 
			
		||||
			m.Post("", web.Bind(forms.AddSecretForm{}), user_setting.SecretsPost)
 | 
			
		||||
			m.Post("/delete", user_setting.SecretsDelete)
 | 
			
		||||
		})
 | 
			
		||||
		m.Get("/organization", user_setting.Organization)
 | 
			
		||||
		m.Get("/repos", user_setting.Repos)
 | 
			
		||||
		m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
 | 
			
		||||
@@ -982,10 +987,12 @@ func RegisterRoutes(m *web.Route) {
 | 
			
		||||
				m.Combo("").Get(repo.DeployKeys).
 | 
			
		||||
					Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost)
 | 
			
		||||
				m.Post("/delete", repo.DeleteDeployKey)
 | 
			
		||||
				m.Group("/secrets", func() {
 | 
			
		||||
					m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost)
 | 
			
		||||
					m.Post("/delete", repo.DeleteSecret)
 | 
			
		||||
				})
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			m.Group("/secrets", func() {
 | 
			
		||||
				m.Get("", repo.Secrets)
 | 
			
		||||
				m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost)
 | 
			
		||||
				m.Post("/delete", repo.DeleteSecret)
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			m.Group("/lfs", func() {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,78 +6,10 @@
 | 
			
		||||
			{{template "org/settings/navbar" .}}
 | 
			
		||||
			<div class="ui twelve wide column content">
 | 
			
		||||
				{{template "base/alert" .}}
 | 
			
		||||
				<h4 class="ui top attached header">
 | 
			
		||||
					{{.locale.Tr "secrets.secrets"}}
 | 
			
		||||
					<div class="ui right">
 | 
			
		||||
						<div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</h4>
 | 
			
		||||
				<div class="ui attached segment">
 | 
			
		||||
					<div class="{{if not .HasError}}hide {{end}}mb-4" id="add-secret-panel">
 | 
			
		||||
						<form class="ui form" action="{{.Link}}" method="post">
 | 
			
		||||
							{{.CsrfTokenHtml}}
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								{{.locale.Tr "secrets.description"}}
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="field{{if .Err_Title}} error{{end}}">
 | 
			
		||||
								<label for="secret-title">{{.locale.Tr "secrets.name"}}</label>
 | 
			
		||||
								<input id="secret-title" name="title" value="{{.title}}" autofocus required pattern="^[a-zA-Z_][a-zA-Z0-9_]*$" placeholder="{{.locale.Tr "secrets.creation.name_placeholder"}}">
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="field{{if .Err_Content}} error{{end}}">
 | 
			
		||||
								<label for="secret-content">{{.locale.Tr "secrets.value"}}</label>
 | 
			
		||||
								<textarea id="secret-content" name="content" required placeholder="{{.locale.Tr "secrets.creation.value_placeholder"}}">{{.content}}</textarea>
 | 
			
		||||
							</div>
 | 
			
		||||
							<button class="ui green button">
 | 
			
		||||
								{{.locale.Tr "secrets.creation"}}
 | 
			
		||||
							</button>
 | 
			
		||||
							<button class="ui hide-panel button" data-panel="#add-secret-panel">
 | 
			
		||||
								{{.locale.Tr "cancel"}}
 | 
			
		||||
							</button>
 | 
			
		||||
						</form>
 | 
			
		||||
					</div>
 | 
			
		||||
					{{if .Secrets}}
 | 
			
		||||
					<div class="ui key list">
 | 
			
		||||
						{{range .Secrets}}
 | 
			
		||||
						<div class="item">
 | 
			
		||||
							<div class="right floated content">
 | 
			
		||||
								<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 | 
			
		||||
									{{$.locale.Tr "settings.delete_key"}}
 | 
			
		||||
								</button>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="left floated content">
 | 
			
		||||
								<i>{{svg "octicon-key" 32}}</i>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="content">
 | 
			
		||||
								<strong>{{.Name}}</strong>
 | 
			
		||||
								<div class="print meta">******</div>
 | 
			
		||||
								<div class="activity meta">
 | 
			
		||||
									<i>
 | 
			
		||||
										{{$.locale.Tr "settings.add_on"}}
 | 
			
		||||
										<span>{{.CreatedUnix.FormatShort}}</span>
 | 
			
		||||
									</i>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
						{{end}}
 | 
			
		||||
					</div>
 | 
			
		||||
					{{else}}
 | 
			
		||||
						{{.locale.Tr "secrets.none"}}
 | 
			
		||||
					{{end}}
 | 
			
		||||
				</div>
 | 
			
		||||
				{{template "shared/secrets/add_list" .}}
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="ui small basic delete modal">
 | 
			
		||||
	<div class="ui header">
 | 
			
		||||
		{{svg "octicon-trash" 16 "mr-2"}}
 | 
			
		||||
		{{.locale.Tr "secrets.deletion"}}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="content">
 | 
			
		||||
		<p>{{.locale.Tr "secrets.deletion.description"}}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	{{template "base/delete_modal_actions" .}}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{{template "base/footer" .}}
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@
 | 
			
		||||
					{{range .Deploykeys}}
 | 
			
		||||
						<div class="item">
 | 
			
		||||
							<div class="right floated content">
 | 
			
		||||
								<button class="ui red tiny button delete-button" data-modal-id="delete-deploy_keys-modal" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 | 
			
		||||
								<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 | 
			
		||||
									{{$.locale.Tr "settings.delete_key"}}
 | 
			
		||||
								</button>
 | 
			
		||||
							</div>
 | 
			
		||||
@@ -75,11 +75,9 @@
 | 
			
		||||
			{{end}}
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<br/>
 | 
			
		||||
	{{template "repo/settings/secrets" .}}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="ui small basic delete modal" id="delete-deploy_keys-modal">
 | 
			
		||||
<div class="ui small basic delete modal">
 | 
			
		||||
	<div class="ui icon header">
 | 
			
		||||
		{{svg "octicon-trash"}}
 | 
			
		||||
		{{.locale.Tr "repo.settings.deploy_key_deletion"}}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
			{{if or .SignedUser.AllowGitHook .SignedUser.IsAdmin}}
 | 
			
		||||
				<li {{if .PageIsSettingsGitHooks}}class="current"{{end}}><a href="{{.RepoLink}}/settings/hooks/git">{{.locale.Tr "repo.settings.githooks"}}</a></li>
 | 
			
		||||
			{{end}}
 | 
			
		||||
			<li {{if .PageIsSettingsKeys}}class="current"{{end}}><a href="{{.RepoLink}}/settings/keys">{{.locale.Tr "secrets.secrets"}}</a></li>
 | 
			
		||||
			<li {{if .PageIsSettingsKeys}}class="current"{{end}}><a href="{{.RepoLink}}/settings/keys">{{.locale.Tr "repo.settings.deploy_keys"}}</a></li>
 | 
			
		||||
			<li {{if .PageIsSettingsSecrets}}class="current"{{end}}><a href="{{.RepoLink}}/settings/secrets">{{.locale.Tr "secrets.secrets"}}</a></li>
 | 
			
		||||
		</ul>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,9 @@
 | 
			
		||||
			</a>
 | 
			
		||||
		{{end}}
 | 
			
		||||
		<a class="{{if .PageIsSettingsKeys}}active {{end}}item" href="{{.RepoLink}}/settings/keys">
 | 
			
		||||
			{{.locale.Tr "repo.settings.deploy_keys"}}
 | 
			
		||||
		</a>
 | 
			
		||||
		<a class="{{if .PageIsSettingsSecrets}}active {{end}}item" href="{{.RepoLink}}/settings/secrets">
 | 
			
		||||
			{{.locale.Tr "secrets.secrets"}}
 | 
			
		||||
		</a>
 | 
			
		||||
		{{if .LFSStartServer}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,80 +1,10 @@
 | 
			
		||||
<div class="ui container">
 | 
			
		||||
	<h4 class="ui top attached header">
 | 
			
		||||
		{{.locale.Tr "secrets.secrets"}}
 | 
			
		||||
		<div class="ui right">
 | 
			
		||||
			<div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</h4>
 | 
			
		||||
	<div class="ui attached segment">
 | 
			
		||||
		<div class="{{if not .HasError}}hide {{end}}mb-4" id="add-secret-panel">
 | 
			
		||||
			<form class="ui form" action="{{.Link}}/secrets" method="post">
 | 
			
		||||
				{{.CsrfTokenHtml}}
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					{{.locale.Tr "secrets.description"}}
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="field{{if .Err_Title}} error{{end}}">
 | 
			
		||||
					<label for="secret-title">{{.locale.Tr "secrets.name"}}</label>
 | 
			
		||||
					<input id="secret-title" name="title" value="{{.title}}" autofocus required pattern="^[a-zA-Z_][a-zA-Z0-9_]*$" placeholder="{{.locale.Tr "secrets.creation.name_placeholder"}}">
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="field{{if .Err_Content}} error{{end}}">
 | 
			
		||||
					<label for="secret-content">{{.locale.Tr "secrets.value"}}</label>
 | 
			
		||||
					<textarea id="secret-content" name="content" required placeholder="{{.locale.Tr "secrets.creation.value_placeholder"}}">{{.content}}</textarea>
 | 
			
		||||
				</div>
 | 
			
		||||
				<button class="ui green button">
 | 
			
		||||
					{{.locale.Tr "secrets.creation"}}
 | 
			
		||||
				</button>
 | 
			
		||||
				<button class="ui hide-panel button" data-panel="#add-secret-panel">
 | 
			
		||||
					{{.locale.Tr "cancel"}}
 | 
			
		||||
				</button>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
		{{if .Secrets}}
 | 
			
		||||
			<div class="ui key list">
 | 
			
		||||
				{{range .Secrets}}
 | 
			
		||||
					<div class="item">
 | 
			
		||||
						<div class="right floated content">
 | 
			
		||||
							<button class="ui red tiny button delete-button" data-modal-id="delete-secret-modal" data-url="{{$.Link}}/secrets/delete" data-id="{{.ID}}">
 | 
			
		||||
								{{$.locale.Tr "settings.delete_key"}}
 | 
			
		||||
							</button>
 | 
			
		||||
						</div>
 | 
			
		||||
						<div class="left floated content">
 | 
			
		||||
							<i>{{svg "octicon-key" 32}}</i>
 | 
			
		||||
						</div>
 | 
			
		||||
						<div class="content">
 | 
			
		||||
							<strong>{{.Name}}</strong>
 | 
			
		||||
							<div class="print meta">******</div>
 | 
			
		||||
							<div class="activity meta">
 | 
			
		||||
								<i>
 | 
			
		||||
									{{$.locale.Tr "settings.add_on"}}
 | 
			
		||||
									<span>{{.CreatedUnix.FormatShort}}</span>
 | 
			
		||||
								</i>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				{{end}}
 | 
			
		||||
			</div>
 | 
			
		||||
		{{else}}
 | 
			
		||||
			{{.locale.Tr "secrets.none"}}
 | 
			
		||||
		{{end}}
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="ui small basic delete modal" id="delete-secret-modal">
 | 
			
		||||
	<div class="ui icon header">
 | 
			
		||||
		{{svg "octicon-trash"}}
 | 
			
		||||
		{{.locale.Tr "secrets.deletion"}}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="content">
 | 
			
		||||
		<p>{{.locale.Tr "secrets.deletion.description"}}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="actions">
 | 
			
		||||
		<div class="ui red basic inverted cancel button">
 | 
			
		||||
			<i class="remove icon"></i>
 | 
			
		||||
			{{.locale.Tr "modal.no"}}
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="ui green basic inverted ok button">
 | 
			
		||||
			<i class="checkmark icon"></i>
 | 
			
		||||
			{{.locale.Tr "modal.yes"}}
 | 
			
		||||
		</div>
 | 
			
		||||
{{template "base/head" .}}
 | 
			
		||||
<div class="page-content repository settings">
 | 
			
		||||
	{{template "repo/header" .}}
 | 
			
		||||
	{{template "repo/settings/navbar" .}}
 | 
			
		||||
	<div class="ui container">
 | 
			
		||||
		{{template "base/alert" .}}
 | 
			
		||||
		{{template "shared/secrets/add_list" .}}
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
{{template "base/footer" .}}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								templates/shared/secrets/add_list.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								templates/shared/secrets/add_list.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
<h4 class="ui top attached header">
 | 
			
		||||
	{{.locale.Tr "secrets.secrets"}}
 | 
			
		||||
	<div class="ui right">
 | 
			
		||||
		<div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</h4>
 | 
			
		||||
<div class="ui attached segment">
 | 
			
		||||
	<div class="{{if not .HasError}}hide {{end}}mb-4" id="add-secret-panel">
 | 
			
		||||
		<form class="ui form" action="{{.Link}}" method="post">
 | 
			
		||||
			{{.CsrfTokenHtml}}
 | 
			
		||||
			<div class="field">
 | 
			
		||||
				{{.locale.Tr "secrets.description"}}
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="field{{if .Err_Title}} error{{end}}">
 | 
			
		||||
				<label for="secret-title">{{.locale.Tr "secrets.name"}}</label>
 | 
			
		||||
				<input id="secret-title" name="title" value="{{.title}}" autofocus required pattern="^[a-zA-Z_][a-zA-Z0-9_]*$" placeholder="{{.locale.Tr "secrets.creation.name_placeholder"}}">
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="field{{if .Err_Content}} error{{end}}">
 | 
			
		||||
				<label for="secret-content">{{.locale.Tr "secrets.value"}}</label>
 | 
			
		||||
				<textarea id="secret-content" name="content" required placeholder="{{.locale.Tr "secrets.creation.value_placeholder"}}">{{.content}}</textarea>
 | 
			
		||||
			</div>
 | 
			
		||||
			<button class="ui green button">
 | 
			
		||||
				{{.locale.Tr "secrets.creation"}}
 | 
			
		||||
			</button>
 | 
			
		||||
			<button class="ui hide-panel button" data-panel="#add-secret-panel">
 | 
			
		||||
				{{.locale.Tr "cancel"}}
 | 
			
		||||
			</button>
 | 
			
		||||
		</form>
 | 
			
		||||
	</div>
 | 
			
		||||
	{{if .Secrets}}
 | 
			
		||||
	<div class="ui key list">
 | 
			
		||||
		{{range .Secrets}}
 | 
			
		||||
		<div class="item">
 | 
			
		||||
			<div class="right floated content">
 | 
			
		||||
				<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 | 
			
		||||
					{{$.locale.Tr "settings.delete_key"}}
 | 
			
		||||
				</button>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="left floated content">
 | 
			
		||||
				<i>{{svg "octicon-key" 32}}</i>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="content">
 | 
			
		||||
				<strong>{{.Name}}</strong>
 | 
			
		||||
				<div class="print meta">******</div>
 | 
			
		||||
				<div class="activity meta">
 | 
			
		||||
					<i>
 | 
			
		||||
						{{$.locale.Tr "settings.add_on"}}
 | 
			
		||||
						<span>{{.CreatedUnix.FormatShort}}</span>
 | 
			
		||||
					</i>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		{{end}}
 | 
			
		||||
	</div>
 | 
			
		||||
	{{else}}
 | 
			
		||||
		{{.locale.Tr "secrets.none"}}
 | 
			
		||||
	{{end}}
 | 
			
		||||
</div>
 | 
			
		||||
<div class="ui small basic delete modal">
 | 
			
		||||
	<div class="ui header">
 | 
			
		||||
		{{svg "octicon-trash" 16 "mr-2"}}
 | 
			
		||||
		{{.locale.Tr "secrets.deletion"}}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="content">
 | 
			
		||||
		<p>{{.locale.Tr "secrets.deletion.description"}}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	{{template "base/delete_modal_actions" .}}
 | 
			
		||||
</div>
 | 
			
		||||
@@ -18,6 +18,9 @@
 | 
			
		||||
		<a class="{{if .PageIsSettingsKeys}}active {{end}}item" href="{{AppSubUrl}}/user/settings/keys">
 | 
			
		||||
			{{.locale.Tr "settings.ssh_gpg_keys"}}
 | 
			
		||||
		</a>
 | 
			
		||||
		<a class="{{if .PageIsSettingsSecrets}}active {{end}}item" href="{{AppSubUrl}}/user/settings/secrets">
 | 
			
		||||
			{{.locale.Tr "secrets.secrets"}}
 | 
			
		||||
		</a>
 | 
			
		||||
		{{if .EnablePackages}}
 | 
			
		||||
		<a class="{{if .PageIsSettingsPackages}}active {{end}}item" href="{{AppSubUrl}}/user/settings/packages">
 | 
			
		||||
			{{.locale.Tr "packages.title"}}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								templates/user/settings/secrets.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								templates/user/settings/secrets.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
{{template "base/head" .}}
 | 
			
		||||
<div class="page-content user settings secrets">
 | 
			
		||||
	{{template "user/settings/navbar" .}}
 | 
			
		||||
	<div class="ui container">
 | 
			
		||||
		{{template "base/alert" .}}
 | 
			
		||||
		{{template "shared/secrets/add_list" .}}
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{{template "base/footer" .}}
 | 
			
		||||
		Reference in New Issue
	
	Block a user