75f128ebf8
- Backport of #22229 - When the GPG key contains an error, such as an invalid signature or an email address that does not match the user.A page will be shown that says you must provide a signature for the token. - This page had two errors: one had the wrong translation key and the other tried to use an undefined variable [`.PaddedKeyID`](e81ccc406b/models/asymkey/gpg_key.go (L65-L72)
), which is a function implemented on the `GPGKey` struct, given that we don't have that, we use [`KeyID`](e81ccc406b/routers/web/user/setting/keys.go (L102)
) which is [the fingerprint of the publickey](https://pkg.go.dev/golang.org/x/crypto/openpgp/packet#PublicKey.KeyIdString) and is a valid way for opengpg to refer to a key.
298 lines
10 KiB
Go
298 lines
10 KiB
Go
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package setting
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/context"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/web"
|
|
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
|
"code.gitea.io/gitea/services/forms"
|
|
)
|
|
|
|
const (
|
|
tplSettingsKeys base.TplName = "user/settings/keys"
|
|
)
|
|
|
|
// Keys render user's SSH/GPG public keys page
|
|
func Keys(ctx *context.Context) {
|
|
ctx.Data["Title"] = ctx.Tr("settings")
|
|
ctx.Data["PageIsSettingsKeys"] = true
|
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
|
ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
|
|
ctx.Data["AllowPrincipals"] = setting.SSH.AuthorizedPrincipalsEnabled
|
|
|
|
loadKeysData(ctx)
|
|
|
|
ctx.HTML(http.StatusOK, tplSettingsKeys)
|
|
}
|
|
|
|
// KeysPost response for change user's SSH/GPG keys
|
|
func KeysPost(ctx *context.Context) {
|
|
form := web.GetForm(ctx).(*forms.AddKeyForm)
|
|
ctx.Data["Title"] = ctx.Tr("settings")
|
|
ctx.Data["PageIsSettingsKeys"] = true
|
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
|
ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
|
|
ctx.Data["AllowPrincipals"] = setting.SSH.AuthorizedPrincipalsEnabled
|
|
|
|
if ctx.HasError() {
|
|
loadKeysData(ctx)
|
|
|
|
ctx.HTML(http.StatusOK, tplSettingsKeys)
|
|
return
|
|
}
|
|
switch form.Type {
|
|
case "principal":
|
|
content, err := asymkey_model.CheckPrincipalKeyString(ctx.Doer, form.Content)
|
|
if err != nil {
|
|
if db.IsErrSSHDisabled(err) {
|
|
ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
|
|
} else {
|
|
ctx.Flash.Error(ctx.Tr("form.invalid_ssh_principal", err.Error()))
|
|
}
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
return
|
|
}
|
|
if _, err = asymkey_model.AddPrincipalKey(ctx.Doer.ID, content, 0); err != nil {
|
|
ctx.Data["HasPrincipalError"] = true
|
|
switch {
|
|
case asymkey_model.IsErrKeyAlreadyExist(err), asymkey_model.IsErrKeyNameAlreadyUsed(err):
|
|
loadKeysData(ctx)
|
|
|
|
ctx.Data["Err_Content"] = true
|
|
ctx.RenderWithErr(ctx.Tr("settings.ssh_principal_been_used"), tplSettingsKeys, &form)
|
|
default:
|
|
ctx.ServerError("AddPrincipalKey", err)
|
|
}
|
|
return
|
|
}
|
|
ctx.Flash.Success(ctx.Tr("settings.add_principal_success", form.Content))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
case "gpg":
|
|
token := asymkey_model.VerificationToken(ctx.Doer, 1)
|
|
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
|
|
|
|
keys, err := asymkey_model.AddGPGKey(ctx.Doer.ID, form.Content, token, form.Signature)
|
|
if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
|
|
keys, err = asymkey_model.AddGPGKey(ctx.Doer.ID, form.Content, lastToken, form.Signature)
|
|
}
|
|
if err != nil {
|
|
ctx.Data["HasGPGError"] = true
|
|
switch {
|
|
case asymkey_model.IsErrGPGKeyParsing(err):
|
|
ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
|
|
loadKeysData(ctx)
|
|
|
|
ctx.Data["Err_Content"] = true
|
|
ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
|
|
case asymkey_model.IsErrGPGInvalidTokenSignature(err):
|
|
loadKeysData(ctx)
|
|
ctx.Data["Err_Content"] = true
|
|
ctx.Data["Err_Signature"] = true
|
|
keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
|
ctx.Data["KeyID"] = keyID
|
|
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
|
ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
|
|
case asymkey_model.IsErrGPGNoEmailFound(err):
|
|
loadKeysData(ctx)
|
|
|
|
ctx.Data["Err_Content"] = true
|
|
ctx.Data["Err_Signature"] = true
|
|
keyID := err.(asymkey_model.ErrGPGNoEmailFound).ID
|
|
ctx.Data["KeyID"] = keyID
|
|
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
|
ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
|
|
default:
|
|
ctx.ServerError("AddPublicKey", err)
|
|
}
|
|
return
|
|
}
|
|
keyIDs := ""
|
|
for _, key := range keys {
|
|
keyIDs += key.KeyID
|
|
keyIDs += ", "
|
|
}
|
|
if len(keyIDs) > 0 {
|
|
keyIDs = keyIDs[:len(keyIDs)-2]
|
|
}
|
|
ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", keyIDs))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
case "verify_gpg":
|
|
token := asymkey_model.VerificationToken(ctx.Doer, 1)
|
|
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
|
|
|
|
keyID, err := asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, token, form.Signature)
|
|
if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
|
|
keyID, err = asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, lastToken, form.Signature)
|
|
}
|
|
if err != nil {
|
|
ctx.Data["HasGPGVerifyError"] = true
|
|
switch {
|
|
case asymkey_model.IsErrGPGInvalidTokenSignature(err):
|
|
loadKeysData(ctx)
|
|
ctx.Data["VerifyingID"] = form.KeyID
|
|
ctx.Data["Err_Signature"] = true
|
|
keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
|
ctx.Data["KeyID"] = keyID
|
|
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
|
ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
|
|
default:
|
|
ctx.ServerError("VerifyGPG", err)
|
|
}
|
|
}
|
|
ctx.Flash.Success(ctx.Tr("settings.verify_gpg_key_success", keyID))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
case "ssh":
|
|
content, err := asymkey_model.CheckPublicKeyString(form.Content)
|
|
if err != nil {
|
|
if db.IsErrSSHDisabled(err) {
|
|
ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
|
|
} else if asymkey_model.IsErrKeyUnableVerify(err) {
|
|
ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
|
|
} else {
|
|
ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
|
|
}
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
return
|
|
}
|
|
|
|
if _, err = asymkey_model.AddPublicKey(ctx.Doer.ID, form.Title, content, 0); err != nil {
|
|
ctx.Data["HasSSHError"] = true
|
|
switch {
|
|
case asymkey_model.IsErrKeyAlreadyExist(err):
|
|
loadKeysData(ctx)
|
|
|
|
ctx.Data["Err_Content"] = true
|
|
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
|
|
case asymkey_model.IsErrKeyNameAlreadyUsed(err):
|
|
loadKeysData(ctx)
|
|
|
|
ctx.Data["Err_Title"] = true
|
|
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
|
|
case asymkey_model.IsErrKeyUnableVerify(err):
|
|
ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
default:
|
|
ctx.ServerError("AddPublicKey", err)
|
|
}
|
|
return
|
|
}
|
|
ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
case "verify_ssh":
|
|
token := asymkey_model.VerificationToken(ctx.Doer, 1)
|
|
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
|
|
|
|
fingerprint, err := asymkey_model.VerifySSHKey(ctx.Doer.ID, form.Fingerprint, token, form.Signature)
|
|
if err != nil && asymkey_model.IsErrSSHInvalidTokenSignature(err) {
|
|
fingerprint, err = asymkey_model.VerifySSHKey(ctx.Doer.ID, form.Fingerprint, lastToken, form.Signature)
|
|
}
|
|
if err != nil {
|
|
ctx.Data["HasSSHVerifyError"] = true
|
|
switch {
|
|
case asymkey_model.IsErrSSHInvalidTokenSignature(err):
|
|
loadKeysData(ctx)
|
|
ctx.Data["Err_Signature"] = true
|
|
ctx.Data["Fingerprint"] = err.(asymkey_model.ErrSSHInvalidTokenSignature).Fingerprint
|
|
ctx.RenderWithErr(ctx.Tr("settings.ssh_invalid_token_signature"), tplSettingsKeys, &form)
|
|
default:
|
|
ctx.ServerError("VerifySSH", err)
|
|
}
|
|
}
|
|
ctx.Flash.Success(ctx.Tr("settings.verify_ssh_key_success", fingerprint))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
|
|
default:
|
|
ctx.Flash.Warning("Function not implemented")
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
}
|
|
}
|
|
|
|
// DeleteKey response for delete user's SSH/GPG key
|
|
func DeleteKey(ctx *context.Context) {
|
|
switch ctx.FormString("type") {
|
|
case "gpg":
|
|
if err := asymkey_model.DeleteGPGKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
|
|
ctx.Flash.Error("DeleteGPGKey: " + err.Error())
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success"))
|
|
}
|
|
case "ssh":
|
|
keyID := ctx.FormInt64("id")
|
|
external, err := asymkey_model.PublicKeyIsExternallyManaged(keyID)
|
|
if err != nil {
|
|
ctx.ServerError("sshKeysExternalManaged", err)
|
|
return
|
|
}
|
|
if external {
|
|
ctx.Flash.Error(ctx.Tr("settings.ssh_externally_managed"))
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
return
|
|
}
|
|
if err := asymkey_service.DeletePublicKey(ctx.Doer, keyID); err != nil {
|
|
ctx.Flash.Error("DeletePublicKey: " + err.Error())
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success"))
|
|
}
|
|
case "principal":
|
|
if err := asymkey_service.DeletePublicKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
|
|
ctx.Flash.Error("DeletePublicKey: " + err.Error())
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("settings.ssh_principal_deletion_success"))
|
|
}
|
|
default:
|
|
ctx.Flash.Warning("Function not implemented")
|
|
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
|
|
}
|
|
ctx.JSON(http.StatusOK, map[string]interface{}{
|
|
"redirect": setting.AppSubURL + "/user/settings/keys",
|
|
})
|
|
}
|
|
|
|
func loadKeysData(ctx *context.Context) {
|
|
keys, err := asymkey_model.ListPublicKeys(ctx.Doer.ID, db.ListOptions{})
|
|
if err != nil {
|
|
ctx.ServerError("ListPublicKeys", err)
|
|
return
|
|
}
|
|
ctx.Data["Keys"] = keys
|
|
|
|
externalKeys, err := asymkey_model.PublicKeysAreExternallyManaged(keys)
|
|
if err != nil {
|
|
ctx.ServerError("ListPublicKeys", err)
|
|
return
|
|
}
|
|
ctx.Data["ExternalKeys"] = externalKeys
|
|
|
|
gpgkeys, err := asymkey_model.ListGPGKeys(ctx, ctx.Doer.ID, db.ListOptions{})
|
|
if err != nil {
|
|
ctx.ServerError("ListGPGKeys", err)
|
|
return
|
|
}
|
|
ctx.Data["GPGKeys"] = gpgkeys
|
|
tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1)
|
|
|
|
// generate a new aes cipher using the csrfToken
|
|
ctx.Data["TokenToSign"] = tokenToSign
|
|
|
|
principals, err := asymkey_model.ListPrincipalKeys(ctx.Doer.ID, db.ListOptions{})
|
|
if err != nil {
|
|
ctx.ServerError("ListPrincipalKeys", err)
|
|
return
|
|
}
|
|
ctx.Data["Principals"] = principals
|
|
|
|
ctx.Data["VerifyingID"] = ctx.FormString("verify_gpg")
|
|
ctx.Data["VerifyingFingerprint"] = ctx.FormString("verify_ssh")
|
|
}
|