2014-03-10 12:54:52 +04:00
// Copyright 2014 The Gogs 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 user
import (
2017-01-16 05:14:29 +03:00
"bytes"
2015-09-07 00:12:02 +03:00
"errors"
"fmt"
2014-11-21 18:58:08 +03:00
"io/ioutil"
2014-08-25 22:07:08 +04:00
"strings"
2014-07-26 08:24:27 +04:00
"github.com/Unknwon/com"
2017-01-16 05:14:29 +03:00
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"encoding/base64"
"html/template"
"image/png"
2014-03-11 04:48:58 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2014-03-10 12:54:52 +04:00
)
2014-06-23 07:11:12 +04:00
const (
2016-11-18 06:03:03 +03:00
tplSettingsProfile base . TplName = "user/settings/profile"
tplSettingsAvatar base . TplName = "user/settings/avatar"
tplSettingsPassword base . TplName = "user/settings/password"
tplSettingsEmails base . TplName = "user/settings/email"
tplSettingsSSHKeys base . TplName = "user/settings/sshkeys"
tplSettingsSocial base . TplName = "user/settings/social"
tplSettingsApplications base . TplName = "user/settings/applications"
2017-01-16 05:14:29 +03:00
tplSettingsTwofa base . TplName = "user/settings/twofa"
tplSettingsTwofaEnroll base . TplName = "user/settings/twofa_enroll"
2017-02-22 10:14:37 +03:00
tplSettingsAccountLink base . TplName = "user/settings/account_link"
2016-11-18 06:03:03 +03:00
tplSettingsDelete base . TplName = "user/settings/delete"
tplSecurity base . TplName = "user/security"
2014-06-23 07:11:12 +04:00
)
2016-11-18 06:03:03 +03:00
// Settings render user's profile page
2016-03-11 19:56:52 +03:00
func Settings ( ctx * context . Context ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsProfile" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsProfile )
2014-04-11 00:36:50 +04:00
}
2016-03-11 19:56:52 +03:00
func handleUsernameChange ( ctx * context . Context , newName string ) {
2016-03-05 08:51:51 +03:00
// Non-local users are not allowed to change their username.
2015-12-12 05:23:19 +03:00
if len ( newName ) == 0 || ! ctx . User . IsLocal ( ) {
2014-03-13 11:44:56 +04:00
return
2014-03-13 11:39:18 +04:00
}
2016-03-05 08:51:51 +03:00
// Check if user name has been changed
2015-12-12 02:52:28 +03:00
if ctx . User . LowerName != strings . ToLower ( newName ) {
if err := models . ChangeUserName ( ctx . User , newName ) ; err != nil {
2015-03-27 00:11:47 +03:00
switch {
case models . IsErrUserAlreadyExist ( err ) :
2015-12-12 02:52:28 +03:00
ctx . Flash . Error ( ctx . Tr ( "newName_been_taken" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
2015-03-27 00:11:47 +03:00
case models . IsErrEmailAlreadyUsed ( err ) :
2015-02-23 02:24:49 +03:00
ctx . Flash . Error ( ctx . Tr ( "form.email_been_used" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
2015-03-27 00:11:47 +03:00
case models . IsErrNameReserved ( err ) :
2015-12-12 02:52:28 +03:00
ctx . Flash . Error ( ctx . Tr ( "user.newName_reserved" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
2015-03-27 00:11:47 +03:00
case models . IsErrNamePatternNotAllowed ( err ) :
2015-12-12 02:52:28 +03:00
ctx . Flash . Error ( ctx . Tr ( "user.newName_pattern_not_allowed" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
2015-02-23 02:24:49 +03:00
default :
2014-07-26 08:24:27 +04:00
ctx . Handle ( 500 , "ChangeUserName" , err )
}
2014-04-04 00:33:27 +04:00
return
}
2015-12-12 02:52:28 +03:00
log . Trace ( "User name changed: %s -> %s" , ctx . User . Name , newName )
2014-03-13 11:39:18 +04:00
}
2016-03-05 08:51:51 +03:00
// In case it's just a case change
2015-12-12 02:52:28 +03:00
ctx . User . Name = newName
ctx . User . LowerName = strings . ToLower ( newName )
}
2016-11-18 06:03:03 +03:00
// SettingsPost response for change user's profile
2016-03-11 19:56:52 +03:00
func SettingsPost ( ctx * context . Context , form auth . UpdateProfileForm ) {
2015-12-12 02:52:28 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsProfile" ] = true
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsProfile )
2015-12-12 02:52:28 +03:00
return
}
2016-03-05 08:51:51 +03:00
handleUsernameChange ( ctx , form . Name )
2015-12-12 02:52:28 +03:00
if ctx . Written ( ) {
return
}
2014-03-13 11:39:18 +04:00
2014-05-24 23:28:31 +04:00
ctx . User . FullName = form . FullName
ctx . User . Email = form . Email
2017-01-08 06:12:03 +03:00
ctx . User . KeepEmailPrivate = form . KeepEmailPrivate
2014-05-24 23:28:31 +04:00
ctx . User . Website = form . Website
ctx . User . Location = form . Location
2017-02-25 17:53:57 +03:00
if err := models . UpdateUserSetting ( ctx . User ) ; err != nil {
2017-02-25 16:39:52 +03:00
if _ , ok := err . ( models . ErrEmailAlreadyUsed ) ; ok {
ctx . Flash . Error ( ctx . Tr ( "form.email_been_used" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
return
}
2014-07-26 08:24:27 +04:00
ctx . Handle ( 500 , "UpdateUser" , err )
2014-03-13 11:39:18 +04:00
return
}
2016-03-05 08:51:51 +03:00
log . Trace ( "User settings updated: %s" , ctx . User . Name )
2014-07-26 08:24:27 +04:00
ctx . Flash . Success ( ctx . Tr ( "settings.update_profile_success" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings" )
2014-03-10 12:54:52 +04:00
}
2016-11-18 06:03:03 +03:00
// UpdateAvatarSetting update user's avatar
2014-11-21 18:58:08 +03:00
// FIXME: limit size.
2016-08-07 20:27:38 +03:00
func UpdateAvatarSetting ( ctx * context . Context , form auth . AvatarForm , ctxUser * models . User ) error {
2016-11-07 19:55:31 +03:00
ctxUser . UseCustomAvatar = form . Source == auth . AvatarLocal
2016-08-07 20:27:38 +03:00
if len ( form . Gravatar ) > 0 {
ctxUser . Avatar = base . EncodeMD5 ( form . Gravatar )
ctxUser . AvatarEmail = form . Gravatar
}
2014-11-21 20:51:36 +03:00
2014-11-21 18:58:08 +03:00
if form . Avatar != nil {
fr , err := form . Avatar . Open ( )
if err != nil {
2015-09-07 00:12:02 +03:00
return fmt . Errorf ( "Avatar.Open: %v" , err )
2014-11-21 18:58:08 +03:00
}
2016-03-05 08:51:51 +03:00
defer fr . Close ( )
2014-11-21 18:58:08 +03:00
data , err := ioutil . ReadAll ( fr )
if err != nil {
2016-03-05 08:51:51 +03:00
return fmt . Errorf ( "ioutil.ReadAll: %v" , err )
2014-11-21 18:58:08 +03:00
}
2016-08-30 12:08:38 +03:00
if ! base . IsImageFile ( data ) {
2015-09-07 00:12:02 +03:00
return errors . New ( ctx . Tr ( "settings.uploaded_avatar_not_a_image" ) )
2014-11-21 18:58:08 +03:00
}
2015-09-07 00:12:02 +03:00
if err = ctxUser . UploadAvatar ( data ) ; err != nil {
return fmt . Errorf ( "UploadAvatar: %v" , err )
2014-11-21 18:58:08 +03:00
}
2014-11-22 18:22:53 +03:00
} else {
2016-03-05 08:51:51 +03:00
// No avatar is uploaded but setting has been changed to enable,
// generate a random one when needed.
2016-08-07 20:27:38 +03:00
if ctxUser . UseCustomAvatar && ! com . IsFile ( ctxUser . CustomAvatarPath ( ) ) {
2016-03-05 08:51:51 +03:00
if err := ctxUser . GenerateRandomAvatar ( ) ; err != nil {
2016-07-23 20:08:22 +03:00
log . Error ( 4 , "GenerateRandomAvatar[%d]: %v" , ctxUser . ID , err )
2016-03-05 08:51:51 +03:00
}
2014-11-22 18:22:53 +03:00
}
2014-11-21 18:58:08 +03:00
}
2014-11-22 18:22:53 +03:00
2015-09-07 00:12:02 +03:00
if err := models . UpdateUser ( ctxUser ) ; err != nil {
return fmt . Errorf ( "UpdateUser: %v" , err )
}
return nil
}
2016-11-18 06:03:03 +03:00
// SettingsAvatar render user avatar page
2016-08-07 20:27:38 +03:00
func SettingsAvatar ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAvatar" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsAvatar )
2016-08-07 20:27:38 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsAvatarPost response for change user's avatar request
2016-08-07 20:27:38 +03:00
func SettingsAvatarPost ( ctx * context . Context , form auth . AvatarForm ) {
2015-09-07 00:12:02 +03:00
if err := UpdateAvatarSetting ( ctx , form , ctx . User ) ; err != nil {
2014-11-22 18:22:53 +03:00
ctx . Flash . Error ( err . Error ( ) )
2015-09-07 00:12:02 +03:00
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.update_avatar_success" ) )
2014-11-22 18:22:53 +03:00
}
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/avatar" )
2014-11-21 18:58:08 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsDeleteAvatar render delete avatar page
2016-03-11 19:56:52 +03:00
func SettingsDeleteAvatar ( ctx * context . Context ) {
2016-03-06 19:36:30 +03:00
if err := ctx . User . DeleteAvatar ( ) ; err != nil {
ctx . Flash . Error ( err . Error ( ) )
2016-03-05 08:51:51 +03:00
}
2016-03-06 19:36:30 +03:00
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/avatar" )
2016-03-05 08:51:51 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsPassword render change user's password page
2016-03-11 19:56:52 +03:00
func SettingsPassword ( ctx * context . Context ) {
2014-12-17 18:42:54 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
2015-09-10 18:40:34 +03:00
ctx . Data [ "PageIsSettingsPassword" ] = true
2017-03-11 12:11:54 +03:00
ctx . Data [ "Email" ] = ctx . User . Email
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsPassword )
2015-09-10 18:40:34 +03:00
}
2014-12-17 18:42:54 +03:00
2016-11-18 06:03:03 +03:00
// SettingsPasswordPost response for change user's password
2016-03-11 19:56:52 +03:00
func SettingsPasswordPost ( ctx * context . Context , form auth . ChangePasswordForm ) {
2015-09-10 18:40:34 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsPassword" ] = true
2017-03-11 12:11:54 +03:00
ctx . Data [ "PageIsSettingsDelete" ] = true
2015-09-10 18:40:34 +03:00
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsPassword )
2014-12-17 18:42:54 +03:00
return
}
2017-02-22 10:14:37 +03:00
if ctx . User . IsPasswordSet ( ) && ! ctx . User . ValidatePassword ( form . OldPassword ) {
2015-09-10 18:40:34 +03:00
ctx . Flash . Error ( ctx . Tr ( "settings.password_incorrect" ) )
} else if form . Password != form . Retype {
ctx . Flash . Error ( ctx . Tr ( "form.password_not_match" ) )
} else {
ctx . User . Passwd = form . Password
2016-12-20 15:32:02 +03:00
var err error
if ctx . User . Salt , err = models . GetUserSalt ( ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
2015-09-10 18:40:34 +03:00
ctx . User . EncodePasswd ( )
if err := models . UpdateUser ( ctx . User ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
log . Trace ( "User password updated: %s" , ctx . User . Name )
ctx . Flash . Success ( ctx . Tr ( "settings.change_password_success" ) )
}
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/password" )
2014-12-17 18:42:54 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsEmails render user's emails page
2016-03-11 19:56:52 +03:00
func SettingsEmails ( ctx * context . Context ) {
2014-12-17 18:42:54 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsEmails" ] = true
2016-07-23 20:08:22 +03:00
emails , err := models . GetEmailAddresses ( ctx . User . ID )
2014-12-17 18:42:54 +03:00
if err != nil {
2015-02-22 06:13:47 +03:00
ctx . Handle ( 500 , "GetEmailAddresses" , err )
2014-12-17 18:42:54 +03:00
return
}
2015-02-22 06:13:47 +03:00
ctx . Data [ "Emails" ] = emails
2014-12-17 18:42:54 +03:00
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsEmails )
2015-09-10 18:40:34 +03:00
}
2014-12-17 18:42:54 +03:00
2016-11-18 06:03:03 +03:00
// SettingsEmailPost response for change user's email
2016-03-11 19:56:52 +03:00
func SettingsEmailPost ( ctx * context . Context , form auth . AddEmailForm ) {
2015-09-10 18:40:34 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsEmails" ] = true
2014-12-17 18:42:54 +03:00
// Make emailaddress primary.
if ctx . Query ( "_method" ) == "PRIMARY" {
2015-09-10 18:40:34 +03:00
if err := models . MakeEmailPrimary ( & models . EmailAddress { ID : ctx . QueryInt64 ( "id" ) } ) ; err != nil {
ctx . Handle ( 500 , "MakeEmailPrimary" , err )
2014-12-17 18:42:54 +03:00
return
}
2015-09-10 18:40:34 +03:00
log . Trace ( "Email made primary: %s" , ctx . User . Name )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/email" )
2014-12-17 18:42:54 +03:00
return
}
// Add Email address.
2016-07-23 20:08:22 +03:00
emails , err := models . GetEmailAddresses ( ctx . User . ID )
2015-09-10 18:40:34 +03:00
if err != nil {
ctx . Handle ( 500 , "GetEmailAddresses" , err )
return
}
ctx . Data [ "Emails" ] = emails
2015-02-22 06:13:47 +03:00
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsEmails )
2015-02-22 06:13:47 +03:00
return
}
2014-12-17 18:42:54 +03:00
2016-07-15 19:36:39 +03:00
email := & models . EmailAddress {
2016-07-23 20:08:22 +03:00
UID : ctx . User . ID ,
2015-12-16 06:57:18 +03:00
Email : form . Email ,
2015-02-22 06:13:47 +03:00
IsActivated : ! setting . Service . RegisterEmailConfirm ,
}
2016-07-15 19:36:39 +03:00
if err := models . AddEmailAddress ( email ) ; err != nil {
2015-03-27 00:11:47 +03:00
if models . IsErrEmailAlreadyUsed ( err ) {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.email_been_used" ) , tplSettingsEmails , & form )
2014-12-17 18:42:54 +03:00
return
2015-02-22 06:13:47 +03:00
}
ctx . Handle ( 500 , "AddEmailAddress" , err )
return
2015-09-10 18:40:34 +03:00
}
2014-12-17 18:42:54 +03:00
2016-07-15 19:36:39 +03:00
// Send confirmation email
2015-09-10 18:40:34 +03:00
if setting . Service . RegisterEmailConfirm {
2016-07-15 19:36:39 +03:00
models . SendActivateEmailMail ( ctx . Context , ctx . User , email )
2014-12-17 18:42:54 +03:00
2015-09-10 18:40:34 +03:00
if err := ctx . Cache . Put ( "MailResendLimit_" + ctx . User . LowerName , ctx . User . LowerName , 180 ) ; err != nil {
log . Error ( 4 , "Set cache(MailResendLimit) fail: %v" , err )
}
2016-07-15 19:36:39 +03:00
ctx . Flash . Info ( ctx . Tr ( "settings.add_email_confirmation_sent" , email . Email , setting . Service . ActiveCodeLives / 60 ) )
2015-09-10 18:40:34 +03:00
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.add_email_success" ) )
2014-12-17 18:42:54 +03:00
}
2016-07-15 19:36:39 +03:00
log . Trace ( "Email address added: %s" , email . Email )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/email" )
2014-04-11 02:09:57 +04:00
}
2016-11-27 14:59:12 +03:00
// DeleteEmail response for delete user's email
2016-03-11 19:56:52 +03:00
func DeleteEmail ( ctx * context . Context ) {
2016-12-15 11:49:06 +03:00
if err := models . DeleteEmailAddress ( & models . EmailAddress { ID : ctx . QueryInt64 ( "id" ) , UID : ctx . User . ID } ) ; err != nil {
2015-09-10 18:40:34 +03:00
ctx . Handle ( 500 , "DeleteEmail" , err )
2014-03-14 09:12:07 +04:00
return
}
2015-09-10 18:40:34 +03:00
log . Trace ( "Email address deleted: %s" , ctx . User . Name )
2014-03-13 12:06:35 +04:00
2015-09-10 18:40:34 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.email_deletion_success" ) )
ctx . JSON ( 200 , map [ string ] interface { } {
2016-11-27 13:14:25 +03:00
"redirect" : setting . AppSubURL + "/user/settings/email" ,
2015-09-10 18:40:34 +03:00
} )
2014-03-13 12:06:35 +04:00
}
2016-11-18 06:03:03 +03:00
// SettingsSSHKeys render user's SSH public keys page
2016-03-11 19:56:52 +03:00
func SettingsSSHKeys ( ctx * context . Context ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsSSHKeys" ] = true
2014-07-20 20:02:59 +04:00
2016-07-23 20:08:22 +03:00
keys , err := models . ListPublicKeys ( ctx . User . ID )
2014-07-20 20:02:59 +04:00
if err != nil {
2015-08-20 12:11:29 +03:00
ctx . Handle ( 500 , "ListPublicKeys" , err )
2014-07-26 08:24:27 +04:00
return
2014-07-20 20:02:59 +04:00
}
2015-08-20 12:11:29 +03:00
ctx . Data [ "Keys" ] = keys
2014-07-20 20:02:59 +04:00
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsSSHKeys )
2014-07-20 20:02:59 +04:00
}
2016-11-18 06:03:03 +03:00
// SettingsSSHKeysPost response for change user's SSH keys
2016-03-11 19:56:52 +03:00
func SettingsSSHKeysPost ( ctx * context . Context , form auth . AddSSHKeyForm ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsSSHKeys" ] = true
2016-07-23 20:08:22 +03:00
keys , err := models . ListPublicKeys ( ctx . User . ID )
2014-07-26 08:24:27 +04:00
if err != nil {
2015-08-20 12:11:29 +03:00
ctx . Handle ( 500 , "ListPublicKeys" , err )
2014-07-26 08:24:27 +04:00
return
}
2015-08-20 12:11:29 +03:00
ctx . Data [ "Keys" ] = keys
2014-03-11 04:48:58 +04:00
2015-08-20 12:11:29 +03:00
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsSSHKeys )
2015-08-20 12:11:29 +03:00
return
}
2014-03-11 04:48:58 +04:00
2015-08-20 12:11:29 +03:00
content , err := models . CheckPublicKeyString ( form . Content )
if err != nil {
2015-11-19 05:21:47 +03:00
if models . IsErrKeyUnableVerify ( err ) {
2015-08-20 12:11:29 +03:00
ctx . Flash . Info ( ctx . Tr ( "form.unable_verify_ssh_key" ) )
2014-03-10 17:12:49 +04:00
} else {
2015-08-20 12:11:29 +03:00
ctx . Flash . Error ( ctx . Tr ( "form.invalid_ssh_key" , err . Error ( ) ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/ssh" )
2015-08-20 12:11:29 +03:00
return
2014-03-10 17:12:49 +04:00
}
}
2014-03-11 04:48:58 +04:00
2016-07-23 20:08:22 +03:00
if _ , err = models . AddPublicKey ( ctx . User . ID , form . Title , content ) ; err != nil {
2015-08-20 12:11:29 +03:00
ctx . Data [ "HasError" ] = true
switch {
case models . IsErrKeyAlreadyExist ( err ) :
ctx . Data [ "Err_Content" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "settings.ssh_key_been_used" ) , tplSettingsSSHKeys , & form )
2015-08-20 12:11:29 +03:00
case models . IsErrKeyNameAlreadyUsed ( err ) :
ctx . Data [ "Err_Title" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "settings.ssh_key_name_used" ) , tplSettingsSSHKeys , & form )
2015-08-20 12:11:29 +03:00
default :
ctx . Handle ( 500 , "AddPublicKey" , err )
2014-03-11 04:48:58 +04:00
}
2015-08-20 12:11:29 +03:00
return
}
2014-03-11 04:48:58 +04:00
2015-08-20 12:11:29 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.add_key_success" , form . Title ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/ssh" )
2015-08-20 12:11:29 +03:00
}
2014-05-06 00:21:43 +04:00
2016-11-18 06:03:03 +03:00
// DeleteSSHKey response for delete user's SSH key
2016-03-11 19:56:52 +03:00
func DeleteSSHKey ( ctx * context . Context ) {
2015-12-03 08:24:37 +03:00
if err := models . DeletePublicKey ( ctx . User , ctx . QueryInt64 ( "id" ) ) ; err != nil {
2015-08-20 12:11:29 +03:00
ctx . Flash . Error ( "DeletePublicKey: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.ssh_key_deletion_success" ) )
2014-03-10 12:54:52 +04:00
}
2014-03-11 04:48:58 +04:00
2015-08-20 12:11:29 +03:00
ctx . JSON ( 200 , map [ string ] interface { } {
2016-11-27 13:14:25 +03:00
"redirect" : setting . AppSubURL + "/user/settings/ssh" ,
2015-08-20 12:11:29 +03:00
} )
2014-03-10 12:54:52 +04:00
}
2014-03-14 13:12:28 +04:00
2016-11-18 06:03:03 +03:00
// SettingsApplications render user's access tokens page
2016-03-11 19:56:52 +03:00
func SettingsApplications ( ctx * context . Context ) {
2014-11-12 14:48:50 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsApplications" ] = true
2016-07-23 20:08:22 +03:00
tokens , err := models . ListAccessTokens ( ctx . User . ID )
2014-11-12 14:48:50 +03:00
if err != nil {
ctx . Handle ( 500 , "ListAccessTokens" , err )
return
}
ctx . Data [ "Tokens" ] = tokens
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsApplications )
2014-11-12 14:48:50 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsApplicationsPost response for add user's access token
2016-03-11 19:56:52 +03:00
func SettingsApplicationsPost ( ctx * context . Context , form auth . NewAccessTokenForm ) {
2014-11-12 14:48:50 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsApplications" ] = true
2015-08-18 22:36:16 +03:00
if ctx . HasError ( ) {
2016-07-23 20:08:22 +03:00
tokens , err := models . ListAccessTokens ( ctx . User . ID )
2015-08-20 12:11:29 +03:00
if err != nil {
ctx . Handle ( 500 , "ListAccessTokens" , err )
return
}
ctx . Data [ "Tokens" ] = tokens
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsApplications )
2015-08-18 22:36:16 +03:00
return
}
2014-11-12 14:48:50 +03:00
2015-08-18 22:36:16 +03:00
t := & models . AccessToken {
2016-07-23 20:08:22 +03:00
UID : ctx . User . ID ,
2015-08-18 22:36:16 +03:00
Name : form . Name ,
2014-11-12 14:48:50 +03:00
}
2015-08-18 22:36:16 +03:00
if err := models . NewAccessToken ( t ) ; err != nil {
ctx . Handle ( 500 , "NewAccessToken" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "settings.generate_token_succees" ) )
ctx . Flash . Info ( t . Sha1 )
2014-11-12 14:48:50 +03:00
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/applications" )
2014-11-12 14:48:50 +03:00
}
2016-11-18 06:03:03 +03:00
// SettingsDeleteApplication response for delete user access token
2016-03-11 19:56:52 +03:00
func SettingsDeleteApplication ( ctx * context . Context ) {
2016-12-15 11:49:06 +03:00
if err := models . DeleteAccessTokenByID ( ctx . QueryInt64 ( "id" ) , ctx . User . ID ) ; err != nil {
2015-08-18 22:36:16 +03:00
ctx . Flash . Error ( "DeleteAccessTokenByID: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.delete_token_success" ) )
}
ctx . JSON ( 200 , map [ string ] interface { } {
2016-11-27 13:14:25 +03:00
"redirect" : setting . AppSubURL + "/user/settings/applications" ,
2015-08-18 22:36:16 +03:00
} )
}
2017-01-16 05:14:29 +03:00
// SettingsTwoFactor renders the 2FA page.
func SettingsTwoFactor ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsTwofa" ] = true
enrolled := true
_ , err := models . GetTwoFactorByUID ( ctx . User . ID )
if err != nil {
if models . IsErrTwoFactorNotEnrolled ( err ) {
enrolled = false
} else {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
}
ctx . Data [ "TwofaEnrolled" ] = enrolled
ctx . HTML ( 200 , tplSettingsTwofa )
}
// SettingsTwoFactorRegenerateScratch regenerates the user's 2FA scratch code.
func SettingsTwoFactorRegenerateScratch ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsTwofa" ] = true
t , err := models . GetTwoFactorByUID ( ctx . User . ID )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err = t . GenerateScratchToken ( ) ; err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err = models . UpdateTwoFactor ( t ) ; err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "settings.twofa_scratch_token_regenerated" , t . ScratchToken ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/two_factor" )
}
// SettingsTwoFactorDisable deletes the user's 2FA settings.
func SettingsTwoFactorDisable ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsTwofa" ] = true
t , err := models . GetTwoFactorByUID ( ctx . User . ID )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err = models . DeleteTwoFactorByID ( t . ID , ctx . User . ID ) ; err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "settings.twofa_disabled" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/two_factor" )
}
func twofaGenerateSecretAndQr ( ctx * context . Context ) bool {
var otpKey * otp . Key
var err error
uri := ctx . Session . Get ( "twofaUri" )
if uri != nil {
otpKey , err = otp . NewKeyFromURL ( uri . ( string ) )
}
if otpKey == nil {
err = nil // clear the error, in case the URL was invalid
otpKey , err = totp . Generate ( totp . GenerateOpts {
Issuer : setting . AppName ,
AccountName : ctx . User . Name ,
} )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return false
}
}
ctx . Data [ "TwofaSecret" ] = otpKey . Secret ( )
img , err := otpKey . Image ( 320 , 240 )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return false
}
var imgBytes bytes . Buffer
if err = png . Encode ( & imgBytes , img ) ; err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return false
}
ctx . Data [ "QrUri" ] = template . URL ( "data:image/png;base64," + base64 . StdEncoding . EncodeToString ( imgBytes . Bytes ( ) ) )
ctx . Session . Set ( "twofaSecret" , otpKey . Secret ( ) )
ctx . Session . Set ( "twofaUri" , otpKey . String ( ) )
return true
}
// SettingsTwoFactorEnroll shows the page where the user can enroll into 2FA.
func SettingsTwoFactorEnroll ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsTwofa" ] = true
t , err := models . GetTwoFactorByUID ( ctx . User . ID )
if t != nil {
// already enrolled
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err != nil && ! models . IsErrTwoFactorNotEnrolled ( err ) {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if ! twofaGenerateSecretAndQr ( ctx ) {
return
}
ctx . HTML ( 200 , tplSettingsTwofaEnroll )
}
// SettingsTwoFactorEnrollPost handles enrolling the user into 2FA.
func SettingsTwoFactorEnrollPost ( ctx * context . Context , form auth . TwoFactorAuthForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsTwofa" ] = true
t , err := models . GetTwoFactorByUID ( ctx . User . ID )
if t != nil {
// already enrolled
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err != nil && ! models . IsErrTwoFactorNotEnrolled ( err ) {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if ctx . HasError ( ) {
if ! twofaGenerateSecretAndQr ( ctx ) {
return
}
ctx . HTML ( 200 , tplSettingsTwofaEnroll )
return
}
secret := ctx . Session . Get ( "twofaSecret" ) . ( string )
if ! totp . Validate ( form . Passcode , secret ) {
if ! twofaGenerateSecretAndQr ( ctx ) {
return
}
ctx . Flash . Error ( ctx . Tr ( "settings.passcode_invalid" ) )
ctx . HTML ( 200 , tplSettingsTwofaEnroll )
return
}
t = & models . TwoFactor {
UID : ctx . User . ID ,
}
err = t . SetSecret ( secret )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
err = t . GenerateScratchToken ( )
if err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
if err = models . NewTwoFactor ( t ) ; err != nil {
ctx . Handle ( 500 , "SettingsTwoFactor" , err )
return
}
ctx . Session . Delete ( "twofaSecret" )
ctx . Session . Delete ( "twofaUri" )
ctx . Flash . Success ( ctx . Tr ( "settings.twofa_enrolled" , t . ScratchToken ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/two_factor" )
}
2017-02-22 10:14:37 +03:00
// SettingsAccountLinks render the account links settings page
func SettingsAccountLinks ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAccountLink" ] = true
accountLinks , err := models . ListAccountLinks ( ctx . User )
if err != nil {
ctx . Handle ( 500 , "ListAccountLinks" , err )
return
}
// map the provider display name with the LoginSource
sources := make ( map [ * models . LoginSource ] string )
for _ , externalAccount := range accountLinks {
if loginSource , err := models . GetLoginSourceByID ( externalAccount . LoginSourceID ) ; err == nil {
var providerDisplayName string
if loginSource . IsOAuth2 ( ) {
providerTechnicalName := loginSource . OAuth2 ( ) . Provider
providerDisplayName = models . OAuth2Providers [ providerTechnicalName ] . DisplayName
} else {
providerDisplayName = loginSource . Name
}
sources [ loginSource ] = providerDisplayName
}
}
ctx . Data [ "AccountLinks" ] = sources
ctx . HTML ( 200 , tplSettingsAccountLink )
}
// SettingsDeleteAccountLink delete a single account link
func SettingsDeleteAccountLink ( ctx * context . Context ) {
if _ , err := models . RemoveAccountLink ( ctx . User , ctx . QueryInt64 ( "loginSourceID" ) ) ; err != nil {
ctx . Flash . Error ( "RemoveAccountLink: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.remove_account_link_success" ) )
}
ctx . JSON ( 200 , map [ string ] interface { } {
"redirect" : setting . AppSubURL + "/user/settings/account_link" ,
} )
}
2016-11-18 06:03:03 +03:00
// SettingsDelete render user suicide page and response for delete user himself
2016-03-11 19:56:52 +03:00
func SettingsDelete ( ctx * context . Context ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsDelete" ] = true
2017-03-11 12:11:54 +03:00
ctx . Data [ "Email" ] = ctx . User . Email
2014-07-26 08:24:27 +04:00
if ctx . Req . Method == "POST" {
2015-08-18 21:49:44 +03:00
if _ , err := models . UserSignIn ( ctx . User . Name , ctx . Query ( "password" ) ) ; err != nil {
if models . IsErrUserNotExist ( err ) {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.enterred_invalid_password" ) , tplSettingsDelete , nil )
2015-08-18 21:49:44 +03:00
} else {
ctx . Handle ( 500 , "UserSignIn" , err )
}
return
}
2014-07-26 08:24:27 +04:00
if err := models . DeleteUser ( ctx . User ) ; err != nil {
2015-03-18 04:51:39 +03:00
switch {
case models . IsErrUserOwnRepos ( err ) :
2014-07-26 08:24:27 +04:00
ctx . Flash . Error ( ctx . Tr ( "form.still_own_repo" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/delete" )
2015-03-18 04:51:39 +03:00
case models . IsErrUserHasOrgs ( err ) :
2014-11-13 13:27:01 +03:00
ctx . Flash . Error ( ctx . Tr ( "form.still_has_org" ) )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/delete" )
2014-07-26 08:24:27 +04:00
default :
ctx . Handle ( 500 , "DeleteUser" , err )
}
} else {
log . Trace ( "Account deleted: %s" , ctx . User . Name )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/" )
2014-07-26 08:24:27 +04:00
}
2014-08-14 10:12:21 +04:00
return
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSettingsDelete )
2014-03-14 13:12:28 +04:00
}