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 }