code.gitea.io/gitea@v1.22.3/routers/web/user/setting/keys.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2018 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package setting
     6  
     7  import (
     8  	"fmt"
     9  	"net/http"
    10  
    11  	asymkey_model "code.gitea.io/gitea/models/asymkey"
    12  	"code.gitea.io/gitea/models/db"
    13  	user_model "code.gitea.io/gitea/models/user"
    14  	"code.gitea.io/gitea/modules/base"
    15  	"code.gitea.io/gitea/modules/setting"
    16  	"code.gitea.io/gitea/modules/web"
    17  	asymkey_service "code.gitea.io/gitea/services/asymkey"
    18  	"code.gitea.io/gitea/services/context"
    19  	"code.gitea.io/gitea/services/forms"
    20  )
    21  
    22  const (
    23  	tplSettingsKeys base.TplName = "user/settings/keys"
    24  )
    25  
    26  // Keys render user's SSH/GPG public keys page
    27  func Keys(ctx *context.Context) {
    28  	ctx.Data["Title"] = ctx.Tr("settings.ssh_gpg_keys")
    29  	ctx.Data["PageIsSettingsKeys"] = true
    30  	ctx.Data["DisableSSH"] = setting.SSH.Disabled
    31  	ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
    32  	ctx.Data["AllowPrincipals"] = setting.SSH.AuthorizedPrincipalsEnabled
    33  
    34  	loadKeysData(ctx)
    35  
    36  	ctx.HTML(http.StatusOK, tplSettingsKeys)
    37  }
    38  
    39  // KeysPost response for change user's SSH/GPG keys
    40  func KeysPost(ctx *context.Context) {
    41  	form := web.GetForm(ctx).(*forms.AddKeyForm)
    42  	ctx.Data["Title"] = ctx.Tr("settings")
    43  	ctx.Data["PageIsSettingsKeys"] = true
    44  	ctx.Data["DisableSSH"] = setting.SSH.Disabled
    45  	ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
    46  	ctx.Data["AllowPrincipals"] = setting.SSH.AuthorizedPrincipalsEnabled
    47  
    48  	if ctx.HasError() {
    49  		loadKeysData(ctx)
    50  
    51  		ctx.HTML(http.StatusOK, tplSettingsKeys)
    52  		return
    53  	}
    54  	switch form.Type {
    55  	case "principal":
    56  		content, err := asymkey_model.CheckPrincipalKeyString(ctx, ctx.Doer, form.Content)
    57  		if err != nil {
    58  			if db.IsErrSSHDisabled(err) {
    59  				ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
    60  			} else {
    61  				ctx.Flash.Error(ctx.Tr("form.invalid_ssh_principal", err.Error()))
    62  			}
    63  			ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
    64  			return
    65  		}
    66  		if _, err = asymkey_service.AddPrincipalKey(ctx, ctx.Doer.ID, content, 0); err != nil {
    67  			ctx.Data["HasPrincipalError"] = true
    68  			switch {
    69  			case asymkey_model.IsErrKeyAlreadyExist(err), asymkey_model.IsErrKeyNameAlreadyUsed(err):
    70  				loadKeysData(ctx)
    71  
    72  				ctx.Data["Err_Content"] = true
    73  				ctx.RenderWithErr(ctx.Tr("settings.ssh_principal_been_used"), tplSettingsKeys, &form)
    74  			default:
    75  				ctx.ServerError("AddPrincipalKey", err)
    76  			}
    77  			return
    78  		}
    79  		ctx.Flash.Success(ctx.Tr("settings.add_principal_success", form.Content))
    80  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
    81  	case "gpg":
    82  		if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
    83  			ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
    84  			return
    85  		}
    86  
    87  		token := asymkey_model.VerificationToken(ctx.Doer, 1)
    88  		lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
    89  
    90  		keys, err := asymkey_model.AddGPGKey(ctx, ctx.Doer.ID, form.Content, token, form.Signature)
    91  		if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
    92  			keys, err = asymkey_model.AddGPGKey(ctx, ctx.Doer.ID, form.Content, lastToken, form.Signature)
    93  		}
    94  		if err != nil {
    95  			ctx.Data["HasGPGError"] = true
    96  			switch {
    97  			case asymkey_model.IsErrGPGKeyParsing(err):
    98  				ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
    99  				ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   100  			case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
   101  				loadKeysData(ctx)
   102  
   103  				ctx.Data["Err_Content"] = true
   104  				ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
   105  			case asymkey_model.IsErrGPGInvalidTokenSignature(err):
   106  				loadKeysData(ctx)
   107  				ctx.Data["Err_Content"] = true
   108  				ctx.Data["Err_Signature"] = true
   109  				keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
   110  				ctx.Data["KeyID"] = keyID
   111  				ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
   112  				ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
   113  			case asymkey_model.IsErrGPGNoEmailFound(err):
   114  				loadKeysData(ctx)
   115  
   116  				ctx.Data["Err_Content"] = true
   117  				ctx.Data["Err_Signature"] = true
   118  				keyID := err.(asymkey_model.ErrGPGNoEmailFound).ID
   119  				ctx.Data["KeyID"] = keyID
   120  				ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
   121  				ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
   122  			default:
   123  				ctx.ServerError("AddPublicKey", err)
   124  			}
   125  			return
   126  		}
   127  		keyIDs := ""
   128  		for _, key := range keys {
   129  			keyIDs += key.KeyID
   130  			keyIDs += ", "
   131  		}
   132  		if len(keyIDs) > 0 {
   133  			keyIDs = keyIDs[:len(keyIDs)-2]
   134  		}
   135  		ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", keyIDs))
   136  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   137  	case "verify_gpg":
   138  		token := asymkey_model.VerificationToken(ctx.Doer, 1)
   139  		lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
   140  
   141  		keyID, err := asymkey_model.VerifyGPGKey(ctx, ctx.Doer.ID, form.KeyID, token, form.Signature)
   142  		if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
   143  			keyID, err = asymkey_model.VerifyGPGKey(ctx, ctx.Doer.ID, form.KeyID, lastToken, form.Signature)
   144  		}
   145  		if err != nil {
   146  			ctx.Data["HasGPGVerifyError"] = true
   147  			switch {
   148  			case asymkey_model.IsErrGPGInvalidTokenSignature(err):
   149  				loadKeysData(ctx)
   150  				ctx.Data["VerifyingID"] = form.KeyID
   151  				ctx.Data["Err_Signature"] = true
   152  				keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
   153  				ctx.Data["KeyID"] = keyID
   154  				ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
   155  				ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
   156  			default:
   157  				ctx.ServerError("VerifyGPG", err)
   158  			}
   159  		}
   160  		ctx.Flash.Success(ctx.Tr("settings.verify_gpg_key_success", keyID))
   161  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   162  	case "ssh":
   163  		if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
   164  			ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
   165  			return
   166  		}
   167  
   168  		content, err := asymkey_model.CheckPublicKeyString(form.Content)
   169  		if err != nil {
   170  			if db.IsErrSSHDisabled(err) {
   171  				ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
   172  			} else if asymkey_model.IsErrKeyUnableVerify(err) {
   173  				ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
   174  			} else if err == asymkey_model.ErrKeyIsPrivate {
   175  				ctx.Flash.Error(ctx.Tr("form.must_use_public_key"))
   176  			} else {
   177  				ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
   178  			}
   179  			ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   180  			return
   181  		}
   182  
   183  		if _, err = asymkey_model.AddPublicKey(ctx, ctx.Doer.ID, form.Title, content, 0); err != nil {
   184  			ctx.Data["HasSSHError"] = true
   185  			switch {
   186  			case asymkey_model.IsErrKeyAlreadyExist(err):
   187  				loadKeysData(ctx)
   188  
   189  				ctx.Data["Err_Content"] = true
   190  				ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
   191  			case asymkey_model.IsErrKeyNameAlreadyUsed(err):
   192  				loadKeysData(ctx)
   193  
   194  				ctx.Data["Err_Title"] = true
   195  				ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
   196  			case asymkey_model.IsErrKeyUnableVerify(err):
   197  				ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
   198  				ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   199  			default:
   200  				ctx.ServerError("AddPublicKey", err)
   201  			}
   202  			return
   203  		}
   204  		ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title))
   205  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   206  	case "verify_ssh":
   207  		if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
   208  			ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
   209  			return
   210  		}
   211  
   212  		token := asymkey_model.VerificationToken(ctx.Doer, 1)
   213  		lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
   214  
   215  		fingerprint, err := asymkey_model.VerifySSHKey(ctx, ctx.Doer.ID, form.Fingerprint, token, form.Signature)
   216  		if err != nil && asymkey_model.IsErrSSHInvalidTokenSignature(err) {
   217  			fingerprint, err = asymkey_model.VerifySSHKey(ctx, ctx.Doer.ID, form.Fingerprint, lastToken, form.Signature)
   218  		}
   219  		if err != nil {
   220  			ctx.Data["HasSSHVerifyError"] = true
   221  			switch {
   222  			case asymkey_model.IsErrSSHInvalidTokenSignature(err):
   223  				loadKeysData(ctx)
   224  				ctx.Data["Err_Signature"] = true
   225  				ctx.Data["Fingerprint"] = err.(asymkey_model.ErrSSHInvalidTokenSignature).Fingerprint
   226  				ctx.RenderWithErr(ctx.Tr("settings.ssh_invalid_token_signature"), tplSettingsKeys, &form)
   227  			default:
   228  				ctx.ServerError("VerifySSH", err)
   229  			}
   230  		}
   231  		ctx.Flash.Success(ctx.Tr("settings.verify_ssh_key_success", fingerprint))
   232  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   233  
   234  	default:
   235  		ctx.Flash.Warning("Function not implemented")
   236  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   237  	}
   238  }
   239  
   240  // DeleteKey response for delete user's SSH/GPG key
   241  func DeleteKey(ctx *context.Context) {
   242  	switch ctx.FormString("type") {
   243  	case "gpg":
   244  		if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
   245  			ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
   246  			return
   247  		}
   248  		if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil {
   249  			ctx.Flash.Error("DeleteGPGKey: " + err.Error())
   250  		} else {
   251  			ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success"))
   252  		}
   253  	case "ssh":
   254  		if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
   255  			ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
   256  			return
   257  		}
   258  
   259  		keyID := ctx.FormInt64("id")
   260  		external, err := asymkey_model.PublicKeyIsExternallyManaged(ctx, keyID)
   261  		if err != nil {
   262  			ctx.ServerError("sshKeysExternalManaged", err)
   263  			return
   264  		}
   265  		if external {
   266  			ctx.Flash.Error(ctx.Tr("settings.ssh_externally_managed"))
   267  			ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   268  			return
   269  		}
   270  		if err := asymkey_service.DeletePublicKey(ctx, ctx.Doer, keyID); err != nil {
   271  			ctx.Flash.Error("DeletePublicKey: " + err.Error())
   272  		} else {
   273  			ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success"))
   274  		}
   275  	case "principal":
   276  		if err := asymkey_service.DeletePublicKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil {
   277  			ctx.Flash.Error("DeletePublicKey: " + err.Error())
   278  		} else {
   279  			ctx.Flash.Success(ctx.Tr("settings.ssh_principal_deletion_success"))
   280  		}
   281  	default:
   282  		ctx.Flash.Warning("Function not implemented")
   283  		ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
   284  	}
   285  	ctx.JSONRedirect(setting.AppSubURL + "/user/settings/keys")
   286  }
   287  
   288  func loadKeysData(ctx *context.Context) {
   289  	keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
   290  		OwnerID:    ctx.Doer.ID,
   291  		NotKeytype: asymkey_model.KeyTypePrincipal,
   292  	})
   293  	if err != nil {
   294  		ctx.ServerError("ListPublicKeys", err)
   295  		return
   296  	}
   297  	ctx.Data["Keys"] = keys
   298  
   299  	externalKeys, err := asymkey_model.PublicKeysAreExternallyManaged(ctx, keys)
   300  	if err != nil {
   301  		ctx.ServerError("ListPublicKeys", err)
   302  		return
   303  	}
   304  	ctx.Data["ExternalKeys"] = externalKeys
   305  
   306  	gpgkeys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
   307  		ListOptions: db.ListOptionsAll,
   308  		OwnerID:     ctx.Doer.ID,
   309  	})
   310  	if err != nil {
   311  		ctx.ServerError("ListGPGKeys", err)
   312  		return
   313  	}
   314  	if err := asymkey_model.GPGKeyList(gpgkeys).LoadSubKeys(ctx); err != nil {
   315  		ctx.ServerError("LoadSubKeys", err)
   316  		return
   317  	}
   318  	ctx.Data["GPGKeys"] = gpgkeys
   319  	tokenToSign := asymkey_model.VerificationToken(ctx.Doer, 1)
   320  
   321  	// generate a new aes cipher using the csrfToken
   322  	ctx.Data["TokenToSign"] = tokenToSign
   323  
   324  	principals, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
   325  		ListOptions: db.ListOptionsAll,
   326  		OwnerID:     ctx.Doer.ID,
   327  		KeyTypes:    []asymkey_model.KeyType{asymkey_model.KeyTypePrincipal},
   328  	})
   329  	if err != nil {
   330  		ctx.ServerError("ListPrincipalKeys", err)
   331  		return
   332  	}
   333  	ctx.Data["Principals"] = principals
   334  
   335  	ctx.Data["VerifyingID"] = ctx.FormString("verify_gpg")
   336  	ctx.Data["VerifyingFingerprint"] = ctx.FormString("verify_ssh")
   337  	ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
   338  }