2018-05-17 07:05:00 +03:00
// 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 (
2019-08-29 17:05:42 +03:00
"errors"
2021-04-05 18:30:52 +03:00
"net/http"
2021-01-17 23:48:38 +03:00
"time"
2019-08-29 17:05:42 +03:00
2018-05-17 07:05:00 +03:00
"code.gitea.io/gitea/models"
2021-11-11 10:03:30 +03:00
user_model "code.gitea.io/gitea/models/user"
2018-05-17 07:05:00 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
2019-10-14 18:24:26 +03:00
"code.gitea.io/gitea/modules/password"
2018-05-17 07:05:00 +03:00
"code.gitea.io/gitea/modules/setting"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2021-07-24 13:16:34 +03:00
"code.gitea.io/gitea/services/auth"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2019-09-24 08:02:49 +03:00
"code.gitea.io/gitea/services/mailer"
2021-11-18 20:42:27 +03:00
"code.gitea.io/gitea/services/user"
2018-05-17 07:05:00 +03:00
)
const (
tplSettingsAccount base . TplName = "user/settings/account"
)
// Account renders change user's password, user's email and user suicide page
func Account ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAccount" ] = true
2022-03-22 10:03:22 +03:00
ctx . Data [ "Email" ] = ctx . Doer . Email
2022-06-27 19:59:47 +03:00
ctx . Data [ "EnableNotifyMail" ] = setting . Service . EnableNotifyMail
2018-05-17 07:05:00 +03:00
2018-06-18 21:24:45 +03:00
loadAccountData ( ctx )
2018-05-17 07:05:00 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsAccount )
2018-05-17 07:05:00 +03:00
}
// AccountPost response for change user's password
2021-01-26 18:36:53 +03:00
func AccountPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . ChangePasswordForm )
2018-05-17 07:05:00 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAccount" ] = true
if ctx . HasError ( ) {
2018-06-18 21:24:45 +03:00
loadAccountData ( ctx )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsAccount )
2018-05-17 07:05:00 +03:00
return
}
if len ( form . Password ) < setting . MinPasswordLength {
ctx . Flash . Error ( ctx . Tr ( "auth.password_too_short" , setting . MinPasswordLength ) )
2022-03-22 10:03:22 +03:00
} else if ctx . Doer . IsPasswordSet ( ) && ! ctx . Doer . ValidatePassword ( form . OldPassword ) {
2018-05-17 07:05:00 +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" ) )
2019-10-14 18:24:26 +03:00
} else if ! password . IsComplexEnough ( form . Password ) {
2019-11-20 01:44:58 +03:00
ctx . Flash . Error ( password . BuildComplexityError ( ctx ) )
2021-05-31 09:18:11 +03:00
} else if pwned , err := password . IsPwned ( ctx , form . Password ) ; pwned || err != nil {
2020-09-09 01:06:39 +03:00
errMsg := ctx . Tr ( "auth.password_pwned" )
if err != nil {
log . Error ( err . Error ( ) )
errMsg = ctx . Tr ( "auth.password_pwned_err" )
}
ctx . Flash . Error ( errMsg )
2018-05-17 07:05:00 +03:00
} else {
var err error
2022-03-22 10:03:22 +03:00
if err = ctx . Doer . SetPassword ( form . Password ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . ServerError ( "UpdateUser" , err )
return
}
2022-03-22 18:22:54 +03:00
if err := user_model . UpdateUserCols ( ctx , ctx . Doer , "salt" , "passwd_hash_algo" , "passwd" ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . ServerError ( "UpdateUser" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "User password updated: %s" , ctx . Doer . Name )
2018-05-17 07:05:00 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.change_password_success" ) )
}
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
}
// EmailPost response for change user's email
2021-01-26 18:36:53 +03:00
func EmailPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . AddEmailForm )
2018-05-17 07:05:00 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAccount" ] = true
// Make emailaddress primary.
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "_method" ) == "PRIMARY" {
2021-11-28 17:11:58 +03:00
if err := user_model . MakeEmailPrimary ( & user_model . EmailAddress { ID : ctx . FormInt64 ( "id" ) } ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . ServerError ( "MakeEmailPrimary" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "Email made primary: %s" , ctx . Doer . Name )
2018-05-17 07:05:00 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
2020-03-02 21:25:36 +03:00
// Send activation Email
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "_method" ) == "SENDACTIVATION" {
2020-03-02 21:25:36 +03:00
var address string
2022-05-21 17:29:49 +03:00
if setting . CacheService . Enabled && ctx . Cache . IsExist ( "MailResendLimit_" + ctx . Doer . LowerName ) {
2020-03-02 21:25:36 +03:00
log . Error ( "Send activation: activation still pending" )
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
2021-07-13 23:59:27 +03:00
2021-07-29 04:42:15 +03:00
id := ctx . FormInt64 ( "id" )
2022-03-22 10:03:22 +03:00
email , err := user_model . GetEmailAddressByID ( ctx . Doer . ID , id )
2021-07-13 23:59:27 +03:00
if err != nil {
2022-03-22 10:03:22 +03:00
log . Error ( "GetEmailAddressByID(%d,%d) error: %v" , ctx . Doer . ID , id , err )
2021-07-13 23:59:27 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
if email == nil {
2022-03-22 10:03:22 +03:00
log . Warn ( "Send activation failed: EmailAddress[%d] not found for user: %-v" , id , ctx . Doer )
2021-07-13 23:59:27 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
if email . IsActivated {
2022-03-22 10:03:22 +03:00
log . Debug ( "Send activation failed: email %s is already activated for user: %-v" , email . Email , ctx . Doer )
2021-07-13 23:59:27 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
if email . IsPrimary {
2022-03-22 10:03:22 +03:00
if ctx . Doer . IsActive && ! setting . Service . RegisterEmailConfirm {
log . Debug ( "Send activation failed: email %s is already activated for user: %-v" , email . Email , ctx . Doer )
2020-03-02 21:25:36 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
2021-07-13 23:59:27 +03:00
// Only fired when the primary email is inactive (Wrong state)
2022-03-22 10:03:22 +03:00
mailer . SendActivateAccountMail ( ctx . Locale , ctx . Doer )
2020-03-02 21:25:36 +03:00
} else {
2022-03-22 10:03:22 +03:00
mailer . SendActivateEmailMail ( ctx . Doer , email )
2020-03-02 21:25:36 +03:00
}
2021-07-13 23:59:27 +03:00
address = email . Email
2020-03-02 21:25:36 +03:00
2022-05-21 17:29:49 +03:00
if setting . CacheService . Enabled {
if err := ctx . Cache . Put ( "MailResendLimit_" + ctx . Doer . LowerName , ctx . Doer . LowerName , 180 ) ; err != nil {
log . Error ( "Set cache(MailResendLimit) fail: %v" , err )
}
2020-03-02 21:25:36 +03:00
}
2022-06-26 17:19:22 +03:00
ctx . Flash . Info ( ctx . Tr ( "settings.add_email_confirmation_sent" , address , timeutil . MinutesToFriendly ( setting . Service . ActiveCodeLives , ctx . Locale ) ) )
2020-03-02 21:25:36 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
2019-08-29 17:05:42 +03:00
// Set Email Notification Preference
2021-08-11 03:31:13 +03:00
if ctx . FormString ( "_method" ) == "NOTIFICATION" {
preference := ctx . FormString ( "preference" )
2021-11-24 12:49:20 +03:00
if ! ( preference == user_model . EmailNotificationsEnabled ||
preference == user_model . EmailNotificationsOnMention ||
preference == user_model . EmailNotificationsDisabled ) {
2022-03-22 10:03:22 +03:00
log . Error ( "Email notifications preference change returned unrecognized option %s: %s" , preference , ctx . Doer . Name )
2019-08-29 17:05:42 +03:00
ctx . ServerError ( "SetEmailPreference" , errors . New ( "option unrecognized" ) )
return
}
2022-03-22 10:03:22 +03:00
if err := user_model . SetEmailNotifications ( ctx . Doer , preference ) ; err != nil {
2019-08-29 17:05:42 +03:00
log . Error ( "Set Email Notifications failed: %v" , err )
ctx . ServerError ( "SetEmailNotifications" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "Email notifications preference made %s: %s" , preference , ctx . Doer . Name )
2020-04-09 20:22:17 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.email_preference_set_success" ) )
2019-08-29 17:05:42 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
return
}
2018-05-17 07:05:00 +03:00
if ctx . HasError ( ) {
2018-06-18 21:24:45 +03:00
loadAccountData ( ctx )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplSettingsAccount )
2018-05-17 07:05:00 +03:00
return
}
2021-11-11 10:03:30 +03:00
email := & user_model . EmailAddress {
2022-03-22 10:03:22 +03:00
UID : ctx . Doer . ID ,
2018-05-17 07:05:00 +03:00
Email : form . Email ,
IsActivated : ! setting . Service . RegisterEmailConfirm ,
}
2022-05-20 17:08:52 +03:00
if err := user_model . AddEmailAddress ( ctx , email ) ; err != nil {
2021-11-11 10:03:30 +03:00
if user_model . IsErrEmailAlreadyUsed ( err ) {
2018-06-18 21:24:45 +03:00
loadAccountData ( ctx )
2018-05-17 07:05:00 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.email_been_used" ) , tplSettingsAccount , & form )
return
2022-03-14 20:39:54 +03:00
} else if user_model . IsErrEmailCharIsNotSupported ( err ) ||
user_model . IsErrEmailInvalid ( err ) {
2020-11-14 19:53:43 +03:00
loadAccountData ( ctx )
ctx . RenderWithErr ( ctx . Tr ( "form.email_invalid" ) , tplSettingsAccount , & form )
return
2018-05-17 07:05:00 +03:00
}
ctx . ServerError ( "AddEmailAddress" , err )
return
}
// Send confirmation email
if setting . Service . RegisterEmailConfirm {
2022-03-22 10:03:22 +03:00
mailer . SendActivateEmailMail ( ctx . Doer , email )
2022-05-21 17:29:49 +03:00
if setting . CacheService . Enabled {
if err := ctx . Cache . Put ( "MailResendLimit_" + ctx . Doer . LowerName , ctx . Doer . LowerName , 180 ) ; err != nil {
log . Error ( "Set cache(MailResendLimit) fail: %v" , err )
}
2018-05-17 07:05:00 +03:00
}
2022-06-26 17:19:22 +03:00
ctx . Flash . Info ( ctx . Tr ( "settings.add_email_confirmation_sent" , email . Email , timeutil . MinutesToFriendly ( setting . Service . ActiveCodeLives , ctx . Locale ) ) )
2018-05-17 07:05:00 +03:00
} else {
ctx . Flash . Success ( ctx . Tr ( "settings.add_email_success" ) )
}
log . Trace ( "Email address added: %s" , email . Email )
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
}
// DeleteEmail response for delete user's email
func DeleteEmail ( ctx * context . Context ) {
2022-03-22 10:03:22 +03:00
if err := user_model . DeleteEmailAddress ( & user_model . EmailAddress { ID : ctx . FormInt64 ( "id" ) , UID : ctx . Doer . ID } ) ; err != nil {
2018-05-17 07:05:00 +03:00
ctx . ServerError ( "DeleteEmail" , err )
return
}
2022-03-22 10:03:22 +03:00
log . Trace ( "Email address deleted: %s" , ctx . Doer . Name )
2018-05-17 07:05:00 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.email_deletion_success" ) )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2018-05-17 07:05:00 +03:00
"redirect" : setting . AppSubURL + "/user/settings/account" ,
} )
}
// DeleteAccount render user suicide page and response for delete user himself
func DeleteAccount ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "settings" )
ctx . Data [ "PageIsSettingsAccount" ] = true
2022-03-22 10:03:22 +03:00
if _ , _ , err := auth . UserSignIn ( ctx . Doer . Name , ctx . FormString ( "password" ) ) ; err != nil {
2021-11-24 12:49:20 +03:00
if user_model . IsErrUserNotExist ( err ) {
2018-06-18 21:24:45 +03:00
loadAccountData ( ctx )
2018-05-17 07:05:00 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.enterred_invalid_password" ) , tplSettingsAccount , nil )
} else {
ctx . ServerError ( "UserSignIn" , err )
}
return
}
2022-07-14 10:22:09 +03:00
if err := user . DeleteUser ( ctx , ctx . Doer , false ) ; err != nil {
2018-05-17 07:05:00 +03:00
switch {
case models . IsErrUserOwnRepos ( err ) :
ctx . Flash . Error ( ctx . Tr ( "form.still_own_repo" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
case models . IsErrUserHasOrgs ( err ) :
ctx . Flash . Error ( ctx . Tr ( "form.still_has_org" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
2022-03-30 11:42:47 +03:00
case models . IsErrUserOwnPackages ( err ) :
ctx . Flash . Error ( ctx . Tr ( "form.still_own_packages" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/account" )
2018-05-17 07:05:00 +03:00
default :
ctx . ServerError ( "DeleteUser" , err )
}
} else {
2022-03-22 10:03:22 +03:00
log . Trace ( "Account deleted: %s" , ctx . Doer . Name )
2018-05-17 07:05:00 +03:00
ctx . Redirect ( setting . AppSubURL + "/" )
}
}
2018-06-18 21:24:45 +03:00
func loadAccountData ( ctx * context . Context ) {
2022-03-22 10:03:22 +03:00
emlist , err := user_model . GetEmailAddresses ( ctx . Doer . ID )
2018-06-18 21:24:45 +03:00
if err != nil {
ctx . ServerError ( "GetEmailAddresses" , err )
return
}
2020-03-02 21:25:36 +03:00
type UserEmail struct {
2021-11-11 10:03:30 +03:00
user_model . EmailAddress
2020-03-02 21:25:36 +03:00
CanBePrimary bool
}
2022-05-21 17:29:49 +03:00
pendingActivation := setting . CacheService . Enabled && ctx . Cache . IsExist ( "MailResendLimit_" + ctx . Doer . LowerName )
2020-03-02 21:25:36 +03:00
emails := make ( [ ] * UserEmail , len ( emlist ) )
for i , em := range emlist {
var email UserEmail
email . EmailAddress = * em
email . CanBePrimary = em . IsActivated
emails [ i ] = & email
}
2018-06-18 21:24:45 +03:00
ctx . Data [ "Emails" ] = emails
2022-03-22 10:03:22 +03:00
ctx . Data [ "EmailNotificationsPreference" ] = ctx . Doer . EmailNotifications ( )
2020-03-02 21:25:36 +03:00
ctx . Data [ "ActivationsPending" ] = pendingActivation
ctx . Data [ "CanAddEmails" ] = ! pendingActivation || ! setting . Service . RegisterEmailConfirm
2021-01-17 23:48:38 +03:00
2021-01-22 05:56:19 +03:00
if setting . Service . UserDeleteWithCommentsMaxTime != 0 {
ctx . Data [ "UserDeleteWithCommentsMaxTime" ] = setting . Service . UserDeleteWithCommentsMaxTime . String ( )
2022-03-22 10:03:22 +03:00
ctx . Data [ "UserDeleteWithComments" ] = ctx . Doer . CreatedUnix . AsTime ( ) . Add ( setting . Service . UserDeleteWithCommentsMaxTime ) . After ( time . Now ( ) )
2021-01-17 23:48:38 +03:00
}
2018-06-18 21:24:45 +03:00
}