2014-07-26 08:24:27 +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
"errors"
2016-03-11 19:56:52 +03:00
"fmt"
2017-02-25 17:57:06 +03:00
"net/http"
2014-07-26 08:24:27 +04:00
"net/url"
2017-02-25 17:57:06 +03:00
"strings"
2014-08-01 01:25:34 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
2017-02-25 17:57:06 +03:00
"code.gitea.io/gitea/modules/auth/oauth2"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2017-02-25 17:57:06 +03:00
"github.com/go-macaron/captcha"
2017-02-22 10:14:37 +03:00
"github.com/markbates/goth"
2014-07-26 08:24:27 +04:00
)
const (
2016-11-18 06:03:03 +03:00
// tplSignIn template for sign in page
tplSignIn base . TplName = "user/auth/signin"
// tplSignUp template path for sign up page
tplSignUp base . TplName = "user/auth/signup"
// TplActivate template path for activate user
TplActivate base . TplName = "user/auth/activate"
tplForgotPassword base . TplName = "user/auth/forgot_passwd"
tplResetPassword base . TplName = "user/auth/reset_passwd"
2017-01-16 05:14:29 +03:00
tplTwofa base . TplName = "user/auth/twofa"
tplTwofaScratch base . TplName = "user/auth/twofa_scratch"
2017-02-22 10:14:37 +03:00
tplLinkAccount base . TplName = "user/auth/link_account"
2014-07-26 08:24:27 +04:00
)
2016-03-11 19:56:52 +03:00
// AutoSignIn reads cookie and try to auto-login.
func AutoSignIn ( ctx * context . Context ) ( bool , error ) {
if ! models . HasEngine {
return false , nil
}
uname := ctx . GetCookie ( setting . CookieUserName )
if len ( uname ) == 0 {
return false , nil
}
isSucceed := false
defer func ( ) {
if ! isSucceed {
log . Trace ( "auto-login cookie cleared: %s" , uname )
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( setting . CookieUserName , "" , - 1 , setting . AppSubURL )
ctx . SetCookie ( setting . CookieRememberName , "" , - 1 , setting . AppSubURL )
2016-03-11 19:56:52 +03:00
}
} ( )
u , err := models . GetUserByName ( uname )
if err != nil {
if ! models . IsErrUserNotExist ( err ) {
return false , fmt . Errorf ( "GetUserByName: %v" , err )
}
return false , nil
}
if val , _ := ctx . GetSuperSecureCookie (
2017-02-25 17:57:06 +03:00
base . EncodeMD5 ( u . Rands + u . Passwd ) , setting . CookieRememberName ) ; val != u . Name {
2016-03-11 19:56:52 +03:00
return false , nil
}
isSucceed = true
2016-07-23 20:08:22 +03:00
ctx . Session . Set ( "uid" , u . ID )
2016-03-11 19:56:52 +03:00
ctx . Session . Set ( "uname" , u . Name )
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( setting . CSRFCookieName , "" , - 1 , setting . AppSubURL )
2016-03-11 19:56:52 +03:00
return true , nil
}
2017-01-16 05:14:29 +03:00
func checkAutoLogin ( ctx * context . Context ) bool {
2014-07-26 08:24:27 +04:00
// Check auto-login.
2016-03-11 19:56:52 +03:00
isSucceed , err := AutoSignIn ( ctx )
2014-07-26 08:24:27 +04:00
if err != nil {
2015-08-13 21:43:40 +03:00
ctx . Handle ( 500 , "AutoSignIn" , err )
2017-01-16 05:14:29 +03:00
return true
2014-07-26 08:24:27 +04:00
}
2016-08-28 01:07:02 +03:00
redirectTo := ctx . Query ( "redirect_to" )
if len ( redirectTo ) > 0 {
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( "redirect_to" , redirectTo , 0 , setting . AppSubURL )
2016-08-28 01:07:02 +03:00
} else {
redirectTo , _ = url . QueryUnescape ( ctx . GetCookie ( "redirect_to" ) )
}
2015-08-13 21:43:40 +03:00
if isSucceed {
2016-08-28 01:07:02 +03:00
if len ( redirectTo ) > 0 {
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( "redirect_to" , "" , - 1 , setting . AppSubURL )
2015-08-13 21:43:40 +03:00
ctx . Redirect ( redirectTo )
2015-11-19 07:52:09 +03:00
} else {
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/" )
2015-08-13 21:43:40 +03:00
}
2017-01-16 05:14:29 +03:00
return true
}
return false
}
// SignIn render sign in page
func SignIn ( ctx * context . Context ) {
2017-05-01 16:26:53 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_in" )
2017-01-16 05:14:29 +03:00
// Check auto-login.
if checkAutoLogin ( ctx ) {
2014-07-26 08:24:27 +04:00
return
}
2017-05-01 16:26:53 +03:00
orderedOAuth2Names , oauth2Providers , err := models . GetActiveOAuth2Providers ( )
2017-02-22 10:14:37 +03:00
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
2017-05-01 16:26:53 +03:00
ctx . Data [ "OrderedOAuth2Names" ] = orderedOAuth2Names
2017-02-22 10:14:37 +03:00
ctx . Data [ "OAuth2Providers" ] = oauth2Providers
2017-03-17 17:16:08 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_in" )
2017-03-07 13:47:56 +03:00
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/login"
2017-03-17 17:16:08 +03:00
ctx . Data [ "PageIsSignIn" ] = true
ctx . Data [ "PageIsLogin" ] = true
2017-02-22 10:14:37 +03:00
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSignIn )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// SignInPost response for sign in request
2016-03-11 19:56:52 +03:00
func SignInPost ( ctx * context . Context , form auth . SignInForm ) {
2017-05-01 16:26:53 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_in" )
orderedOAuth2Names , oauth2Providers , err := models . GetActiveOAuth2Providers ( )
2017-02-22 10:14:37 +03:00
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
2017-05-01 16:26:53 +03:00
ctx . Data [ "OrderedOAuth2Names" ] = orderedOAuth2Names
2017-02-22 10:14:37 +03:00
ctx . Data [ "OAuth2Providers" ] = oauth2Providers
2017-03-07 13:47:56 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_in" )
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/login"
ctx . Data [ "PageIsSignIn" ] = true
ctx . Data [ "PageIsLogin" ] = true
2017-02-22 10:14:37 +03:00
2014-07-26 08:24:27 +04:00
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSignIn )
2014-07-26 08:24:27 +04:00
return
}
u , err := models . UserSignIn ( form . UserName , form . Password )
if err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.username_password_incorrect" ) , tplSignIn , & form )
2017-02-25 17:57:06 +03:00
} else if models . IsErrEmailAlreadyUsed ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "form.email_been_used" ) , tplSignIn , & form )
2014-08-10 08:02:00 +04:00
} else {
ctx . Handle ( 500 , "UserSignIn" , err )
2014-07-26 08:24:27 +04:00
}
return
}
2017-01-16 05:14:29 +03:00
// If this user is enrolled in 2FA, we can't sign the user in just yet.
// Instead, redirect them to the 2FA authentication page.
_ , err = models . GetTwoFactorByUID ( u . ID )
if err != nil {
if models . IsErrTwoFactorNotEnrolled ( err ) {
handleSignIn ( ctx , u , form . Remember )
} else {
ctx . Handle ( 500 , "UserSignIn" , err )
}
return
}
// User needs to use 2FA, save data and redirect to 2FA page.
ctx . Session . Set ( "twofaUid" , u . ID )
ctx . Session . Set ( "twofaRemember" , form . Remember )
ctx . Redirect ( setting . AppSubURL + "/user/two_factor" )
}
// TwoFactor shows the user a two-factor authentication page.
func TwoFactor ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "twofa" )
// Check auto-login.
if checkAutoLogin ( ctx ) {
return
}
// Ensure user is in a 2FA session.
if ctx . Session . Get ( "twofaUid" ) == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in 2FA session" ) )
return
}
ctx . HTML ( 200 , tplTwofa )
}
// TwoFactorPost validates a user's two-factor authentication token.
func TwoFactorPost ( ctx * context . Context , form auth . TwoFactorAuthForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "twofa" )
// Ensure user is in a 2FA session.
idSess := ctx . Session . Get ( "twofaUid" )
if idSess == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in 2FA session" ) )
return
}
id := idSess . ( int64 )
twofa , err := models . GetTwoFactorByUID ( id )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
// Validate the passcode with the stored TOTP secret.
ok , err := twofa . ValidateTOTP ( form . Passcode )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
if ok {
remember := ctx . Session . Get ( "twofaRemember" ) . ( bool )
u , err := models . GetUserByID ( id )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
2017-02-27 13:10:26 +03:00
if ctx . Session . Get ( "linkAccount" ) != nil {
gothUser := ctx . Session . Get ( "linkAccountGothUser" )
if gothUser == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in LinkAccount session" ) )
return
}
err = models . LinkAccountToUser ( u , gothUser . ( goth . User ) )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
}
2017-01-16 05:14:29 +03:00
handleSignIn ( ctx , u , remember )
return
}
ctx . RenderWithErr ( ctx . Tr ( "auth.twofa_passcode_incorrect" ) , tplTwofa , auth . TwoFactorAuthForm { } )
}
// TwoFactorScratch shows the scratch code form for two-factor authentication.
func TwoFactorScratch ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "twofa_scratch" )
// Check auto-login.
if checkAutoLogin ( ctx ) {
return
}
// Ensure user is in a 2FA session.
if ctx . Session . Get ( "twofaUid" ) == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in 2FA session" ) )
return
}
ctx . HTML ( 200 , tplTwofaScratch )
}
// TwoFactorScratchPost validates and invalidates a user's two-factor scratch token.
func TwoFactorScratchPost ( ctx * context . Context , form auth . TwoFactorScratchAuthForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "twofa_scratch" )
// Ensure user is in a 2FA session.
idSess := ctx . Session . Get ( "twofaUid" )
if idSess == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in 2FA session" ) )
return
}
id := idSess . ( int64 )
twofa , err := models . GetTwoFactorByUID ( id )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
// Validate the passcode with the stored TOTP secret.
if twofa . VerifyScratchToken ( form . Token ) {
// Invalidate the scratch token.
twofa . ScratchToken = ""
if err = models . UpdateTwoFactor ( twofa ) ; err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
remember := ctx . Session . Get ( "twofaRemember" ) . ( bool )
u , err := models . GetUserByID ( id )
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
handleSignInFull ( ctx , u , remember , false )
ctx . Flash . Info ( ctx . Tr ( "auth.twofa_scratch_used" ) )
ctx . Redirect ( setting . AppSubURL + "/user/settings/two_factor" )
return
}
ctx . RenderWithErr ( ctx . Tr ( "auth.twofa_scratch_token_incorrect" ) , tplTwofaScratch , auth . TwoFactorScratchAuthForm { } )
}
// This handles the final part of the sign-in process of the user.
func handleSignIn ( ctx * context . Context , u * models . User , remember bool ) {
handleSignInFull ( ctx , u , remember , true )
}
func handleSignInFull ( ctx * context . Context , u * models . User , remember bool , obeyRedirect bool ) {
if remember {
2014-07-26 08:24:27 +04:00
days := 86400 * setting . LogInRememberDays
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( setting . CookieUserName , u . Name , days , setting . AppSubURL )
2017-02-25 17:57:06 +03:00
ctx . SetSuperSecureCookie ( base . EncodeMD5 ( u . Rands + u . Passwd ) ,
2016-11-27 13:14:25 +03:00
setting . CookieRememberName , u . Name , days , setting . AppSubURL )
2014-07-26 08:24:27 +04:00
}
2017-03-17 17:16:08 +03:00
ctx . Session . Delete ( "openid_verified_uri" )
ctx . Session . Delete ( "openid_signin_remember" )
ctx . Session . Delete ( "openid_determined_email" )
ctx . Session . Delete ( "openid_determined_username" )
2017-01-16 05:14:29 +03:00
ctx . Session . Delete ( "twofaUid" )
ctx . Session . Delete ( "twofaRemember" )
2016-07-23 20:08:22 +03:00
ctx . Session . Set ( "uid" , u . ID )
2014-07-26 08:24:27 +04:00
ctx . Session . Set ( "uname" , u . Name )
2016-03-13 04:56:03 +03:00
// Clear whatever CSRF has right now, force to generate a new one
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( setting . CSRFCookieName , "" , - 1 , setting . AppSubURL )
2016-03-13 04:56:03 +03:00
2016-11-09 13:53:45 +03:00
// Register last login
u . SetLastLogin ( )
if err := models . UpdateUser ( u ) ; err != nil {
2016-11-11 15:11:45 +03:00
ctx . Handle ( 500 , "UpdateUser" , err )
return
2016-11-09 13:53:45 +03:00
}
2014-07-26 08:24:27 +04:00
if redirectTo , _ := url . QueryUnescape ( ctx . GetCookie ( "redirect_to" ) ) ; len ( redirectTo ) > 0 {
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( "redirect_to" , "" , - 1 , setting . AppSubURL )
2017-01-16 05:14:29 +03:00
if obeyRedirect {
ctx . Redirect ( redirectTo )
}
2014-07-26 08:24:27 +04:00
return
}
2017-01-16 05:14:29 +03:00
if obeyRedirect {
ctx . Redirect ( setting . AppSubURL + "/" )
}
2014-07-26 08:24:27 +04:00
}
2017-02-22 10:14:37 +03:00
// SignInOAuth handles the OAuth2 login buttons
func SignInOAuth ( ctx * context . Context ) {
provider := ctx . Params ( ":provider" )
loginSource , err := models . GetActiveOAuth2LoginSourceByName ( provider )
if err != nil {
ctx . Handle ( 500 , "SignIn" , err )
return
}
// try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user
user , gothUser , err := oAuth2UserLoginCallback ( loginSource , ctx . Req . Request , ctx . Resp )
if err == nil && user != nil {
// we got the user without going through the whole OAuth2 authentication flow again
handleOAuth2SignIn ( user , gothUser , ctx , err )
return
}
err = oauth2 . Auth ( loginSource . Name , ctx . Req . Request , ctx . Resp )
if err != nil {
ctx . Handle ( 500 , "SignIn" , err )
}
// redirect is done in oauth2.Auth
}
// SignInOAuthCallback handles the callback from the given provider
func SignInOAuthCallback ( ctx * context . Context ) {
provider := ctx . Params ( ":provider" )
// first look if the provider is still active
loginSource , err := models . GetActiveOAuth2LoginSourceByName ( provider )
if err != nil {
ctx . Handle ( 500 , "SignIn" , err )
return
}
if loginSource == nil {
ctx . Handle ( 500 , "SignIn" , errors . New ( "No valid provider found, check configured callback url in provider" ) )
return
}
u , gothUser , err := oAuth2UserLoginCallback ( loginSource , ctx . Req . Request , ctx . Resp )
handleOAuth2SignIn ( u , gothUser , ctx , err )
}
func handleOAuth2SignIn ( u * models . User , gothUser goth . User , ctx * context . Context , err error ) {
if err != nil {
ctx . Handle ( 500 , "UserSignIn" , err )
return
}
if u == nil {
// no existing user is found, request attach or new account
ctx . Session . Set ( "linkAccountGothUser" , gothUser )
ctx . Redirect ( setting . AppSubURL + "/user/link_account" )
return
}
// If this user is enrolled in 2FA, we can't sign the user in just yet.
// Instead, redirect them to the 2FA authentication page.
_ , err = models . GetTwoFactorByUID ( u . ID )
if err != nil {
if models . IsErrTwoFactorNotEnrolled ( err ) {
ctx . Session . Set ( "uid" , u . ID )
ctx . Session . Set ( "uname" , u . Name )
// Clear whatever CSRF has right now, force to generate a new one
ctx . SetCookie ( setting . CSRFCookieName , "" , - 1 , setting . AppSubURL )
// Register last login
u . SetLastLogin ( )
if err := models . UpdateUser ( u ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
if redirectTo , _ := url . QueryUnescape ( ctx . GetCookie ( "redirect_to" ) ) ; len ( redirectTo ) > 0 {
ctx . SetCookie ( "redirect_to" , "" , - 1 , setting . AppSubURL )
ctx . Redirect ( redirectTo )
return
}
ctx . Redirect ( setting . AppSubURL + "/" )
} else {
ctx . Handle ( 500 , "UserSignIn" , err )
}
return
}
// User needs to use 2FA, save data and redirect to 2FA page.
ctx . Session . Set ( "twofaUid" , u . ID )
ctx . Session . Set ( "twofaRemember" , false )
ctx . Redirect ( setting . AppSubURL + "/user/two_factor" )
}
// OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful
// login the user
func oAuth2UserLoginCallback ( loginSource * models . LoginSource , request * http . Request , response http . ResponseWriter ) ( * models . User , goth . User , error ) {
gothUser , err := oauth2 . ProviderCallback ( loginSource . Name , request , response )
if err != nil {
return nil , goth . User { } , err
}
user := & models . User {
LoginName : gothUser . UserID ,
LoginType : models . LoginOAuth2 ,
LoginSource : loginSource . ID ,
}
hasUser , err := models . GetUser ( user )
if err != nil {
return nil , goth . User { } , err
}
if hasUser {
return user , goth . User { } , nil
}
// search in external linked users
externalLoginUser := & models . ExternalLoginUser {
ExternalID : gothUser . UserID ,
LoginSourceID : loginSource . ID ,
}
hasUser , err = models . GetExternalLogin ( externalLoginUser )
if err != nil {
return nil , goth . User { } , err
}
if hasUser {
user , err = models . GetUserByID ( externalLoginUser . UserID )
return user , goth . User { } , err
}
// no user found to login
return nil , gothUser , nil
}
// LinkAccount shows the page where the user can decide to login or create a new account
func LinkAccount ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "link_account" )
ctx . Data [ "LinkAccountMode" ] = true
ctx . Data [ "EnableCaptcha" ] = setting . Service . EnableCaptcha
ctx . Data [ "DisableRegistration" ] = setting . Service . DisableRegistration
ctx . Data [ "ShowRegistrationButton" ] = false
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/link_account_signin"
ctx . Data [ "SignUpLink" ] = setting . AppSubURL + "/user/link_account_signup"
gothUser := ctx . Session . Get ( "linkAccountGothUser" )
if gothUser == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in LinkAccount session" ) )
return
}
ctx . Data [ "user_name" ] = gothUser . ( goth . User ) . NickName
ctx . Data [ "email" ] = gothUser . ( goth . User ) . Email
ctx . HTML ( 200 , tplLinkAccount )
}
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
func LinkAccountPostSignIn ( ctx * context . Context , signInForm auth . SignInForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "link_account" )
ctx . Data [ "LinkAccountMode" ] = true
ctx . Data [ "LinkAccountModeSignIn" ] = true
ctx . Data [ "EnableCaptcha" ] = setting . Service . EnableCaptcha
ctx . Data [ "DisableRegistration" ] = setting . Service . DisableRegistration
ctx . Data [ "ShowRegistrationButton" ] = false
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/link_account_signin"
ctx . Data [ "SignUpLink" ] = setting . AppSubURL + "/user/link_account_signup"
gothUser := ctx . Session . Get ( "linkAccountGothUser" )
if gothUser == nil {
ctx . Handle ( 500 , "UserSignIn" , errors . New ( "not in LinkAccount session" ) )
return
}
if ctx . HasError ( ) {
ctx . HTML ( 200 , tplLinkAccount )
return
}
u , err := models . UserSignIn ( signInForm . UserName , signInForm . Password )
if err != nil {
if models . IsErrUserNotExist ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "form.username_password_incorrect" ) , tplLinkAccount , & signInForm )
} else {
ctx . Handle ( 500 , "UserLinkAccount" , err )
}
return
}
// If this user is enrolled in 2FA, we can't sign the user in just yet.
// Instead, redirect them to the 2FA authentication page.
_ , err = models . GetTwoFactorByUID ( u . ID )
if err != nil {
if models . IsErrTwoFactorNotEnrolled ( err ) {
2017-02-27 13:10:26 +03:00
err = models . LinkAccountToUser ( u , gothUser . ( goth . User ) )
if err != nil {
ctx . Handle ( 500 , "UserLinkAccount" , err )
} else {
handleSignIn ( ctx , u , signInForm . Remember )
}
2017-02-22 10:14:37 +03:00
} else {
ctx . Handle ( 500 , "UserLinkAccount" , err )
}
return
}
// User needs to use 2FA, save data and redirect to 2FA page.
ctx . Session . Set ( "twofaUid" , u . ID )
ctx . Session . Set ( "twofaRemember" , signInForm . Remember )
ctx . Session . Set ( "linkAccount" , true )
ctx . Redirect ( setting . AppSubURL + "/user/two_factor" )
}
// LinkAccountPostRegister handle the creation of a new account for an external account using signUp
func LinkAccountPostRegister ( ctx * context . Context , cpt * captcha . Captcha , form auth . RegisterForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "link_account" )
ctx . Data [ "LinkAccountMode" ] = true
ctx . Data [ "LinkAccountModeRegister" ] = true
ctx . Data [ "EnableCaptcha" ] = setting . Service . EnableCaptcha
ctx . Data [ "DisableRegistration" ] = setting . Service . DisableRegistration
ctx . Data [ "ShowRegistrationButton" ] = false
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/link_account_signin"
ctx . Data [ "SignUpLink" ] = setting . AppSubURL + "/user/link_account_signup"
gothUser := ctx . Session . Get ( "linkAccountGothUser" )
if gothUser == nil {
ctx . Handle ( 500 , "UserSignUp" , errors . New ( "not in LinkAccount session" ) )
return
}
if ctx . HasError ( ) {
ctx . HTML ( 200 , tplLinkAccount )
return
}
if setting . Service . DisableRegistration {
ctx . Error ( 403 )
return
}
if setting . Service . EnableCaptcha && ! cpt . VerifyReq ( ctx . Req ) {
ctx . Data [ "Err_Captcha" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.captcha_incorrect" ) , tplLinkAccount , & form )
return
}
if ( len ( strings . TrimSpace ( form . Password ) ) > 0 || len ( strings . TrimSpace ( form . Retype ) ) > 0 ) && form . Password != form . Retype {
ctx . Data [ "Err_Password" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.password_not_match" ) , tplLinkAccount , & form )
return
}
if len ( strings . TrimSpace ( form . Password ) ) > 0 && len ( form . Password ) < setting . MinPasswordLength {
ctx . Data [ "Err_Password" ] = true
ctx . RenderWithErr ( ctx . Tr ( "auth.password_too_short" , setting . MinPasswordLength ) , tplLinkAccount , & form )
return
}
loginSource , err := models . GetActiveOAuth2LoginSourceByName ( gothUser . ( goth . User ) . Provider )
if err != nil {
ctx . Handle ( 500 , "CreateUser" , err )
}
u := & models . User {
Name : form . UserName ,
Email : form . Email ,
Passwd : form . Password ,
IsActive : ! setting . Service . RegisterEmailConfirm ,
LoginType : models . LoginOAuth2 ,
LoginSource : loginSource . ID ,
LoginName : gothUser . ( goth . User ) . UserID ,
}
if err := models . CreateUser ( u ) ; err != nil {
switch {
case models . IsErrUserAlreadyExist ( err ) :
ctx . Data [ "Err_UserName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.username_been_taken" ) , tplLinkAccount , & form )
case models . IsErrEmailAlreadyUsed ( err ) :
ctx . Data [ "Err_Email" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.email_been_used" ) , tplLinkAccount , & form )
case models . IsErrNameReserved ( err ) :
ctx . Data [ "Err_UserName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "user.form.name_reserved" , err . ( models . ErrNameReserved ) . Name ) , tplLinkAccount , & form )
case models . IsErrNamePatternNotAllowed ( err ) :
ctx . Data [ "Err_UserName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "user.form.name_pattern_not_allowed" , err . ( models . ErrNamePatternNotAllowed ) . Pattern ) , tplLinkAccount , & form )
default :
ctx . Handle ( 500 , "CreateUser" , err )
}
return
}
log . Trace ( "Account created: %s" , u . Name )
// Auto-set admin for the only user.
if models . CountUsers ( ) == 1 {
u . IsAdmin = true
u . IsActive = true
if err := models . UpdateUser ( u ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
}
// Send confirmation email
if setting . Service . RegisterEmailConfirm && u . ID > 1 {
models . SendActivateAccountMail ( ctx . Context , u )
ctx . Data [ "IsSendRegisterMail" ] = true
ctx . Data [ "Email" ] = u . Email
2017-05-29 10:35:47 +03:00
ctx . Data [ "ActiveCodeLives" ] = base . MinutesToFriendly ( setting . Service . ActiveCodeLives )
2017-02-22 10:14:37 +03:00
ctx . HTML ( 200 , TplActivate )
if err := ctx . Cache . Put ( "MailResendLimit_" + u . LowerName , u . LowerName , 180 ) ; err != nil {
log . Error ( 4 , "Set cache(MailResendLimit) fail: %v" , err )
}
return
}
ctx . Redirect ( setting . AppSubURL + "/user/login" )
}
2016-11-18 06:03:03 +03:00
// SignOut sign out from login status
2016-03-11 19:56:52 +03:00
func SignOut ( ctx * context . Context ) {
2014-07-26 08:24:27 +04:00
ctx . Session . Delete ( "uid" )
ctx . Session . Delete ( "uname" )
2014-08-07 14:40:05 +04:00
ctx . Session . Delete ( "socialId" )
ctx . Session . Delete ( "socialName" )
ctx . Session . Delete ( "socialEmail" )
2016-11-27 13:14:25 +03:00
ctx . SetCookie ( setting . CookieUserName , "" , - 1 , setting . AppSubURL )
ctx . SetCookie ( setting . CookieRememberName , "" , - 1 , setting . AppSubURL )
ctx . SetCookie ( setting . CSRFCookieName , "" , - 1 , setting . AppSubURL )
ctx . Redirect ( setting . AppSubURL + "/" )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// SignUp render the register page
2016-03-11 19:56:52 +03:00
func SignUp ( ctx * context . Context ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_up" )
2017-03-07 13:47:56 +03:00
ctx . Data [ "SignUpLink" ] = setting . AppSubURL + "/user/sign_up"
2015-09-13 18:07:21 +03:00
ctx . Data [ "EnableCaptcha" ] = setting . Service . EnableCaptcha
2015-09-13 16:51:51 +03:00
2017-02-22 10:14:37 +03:00
ctx . Data [ "DisableRegistration" ] = setting . Service . DisableRegistration
2014-07-26 08:24:27 +04:00
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSignUp )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// SignUpPost response for sign up information submission
2016-03-11 19:56:52 +03:00
func SignUpPost ( ctx * context . Context , cpt * captcha . Captcha , form auth . RegisterForm ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "sign_up" )
2017-03-07 13:47:56 +03:00
ctx . Data [ "SignUpLink" ] = setting . AppSubURL + "/user/sign_up"
2015-09-13 18:07:21 +03:00
ctx . Data [ "EnableCaptcha" ] = setting . Service . EnableCaptcha
2015-09-13 16:51:51 +03:00
2014-07-26 08:24:27 +04:00
if setting . Service . DisableRegistration {
ctx . Error ( 403 )
return
}
if ctx . HasError ( ) {
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplSignUp )
2014-07-26 08:24:27 +04:00
return
}
2015-09-13 18:07:21 +03:00
if setting . Service . EnableCaptcha && ! cpt . VerifyReq ( ctx . Req ) {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Err_Captcha" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.captcha_incorrect" ) , tplSignUp , & form )
2014-07-26 08:24:27 +04:00
return
2015-09-13 16:51:51 +03:00
}
if form . Password != form . Retype {
2014-07-26 08:24:27 +04:00
ctx . Data [ "Err_Password" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.password_not_match" ) , tplSignUp , & form )
2014-07-26 08:24:27 +04:00
return
}
2016-12-24 16:40:44 +03:00
if len ( form . Password ) < setting . MinPasswordLength {
ctx . Data [ "Err_Password" ] = true
ctx . RenderWithErr ( ctx . Tr ( "auth.password_too_short" , setting . MinPasswordLength ) , tplSignUp , & form )
return
}
2014-07-26 08:24:27 +04:00
u := & models . User {
Name : form . UserName ,
Email : form . Email ,
Passwd : form . Password ,
2015-09-17 23:11:44 +03:00
IsActive : ! setting . Service . RegisterEmailConfirm ,
2014-07-26 08:24:27 +04:00
}
if err := models . CreateUser ( u ) ; err != nil {
2015-03-27 00:11:47 +03:00
switch {
case models . IsErrUserAlreadyExist ( err ) :
2014-07-26 08:24:27 +04:00
ctx . Data [ "Err_UserName" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.username_been_taken" ) , tplSignUp , & form )
2015-03-27 00:11:47 +03:00
case models . IsErrEmailAlreadyUsed ( err ) :
2014-07-26 08:24:27 +04:00
ctx . Data [ "Err_Email" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.email_been_used" ) , tplSignUp , & form )
2015-03-27 00:11:47 +03:00
case models . IsErrNameReserved ( err ) :
2014-07-26 08:24:27 +04:00
ctx . Data [ "Err_UserName" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "user.form.name_reserved" , err . ( models . ErrNameReserved ) . Name ) , tplSignUp , & form )
2015-03-27 00:11:47 +03:00
case models . IsErrNamePatternNotAllowed ( err ) :
ctx . Data [ "Err_UserName" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "user.form.name_pattern_not_allowed" , err . ( models . ErrNamePatternNotAllowed ) . Pattern ) , tplSignUp , & form )
2014-07-26 08:24:27 +04:00
default :
ctx . Handle ( 500 , "CreateUser" , err )
}
return
}
log . Trace ( "Account created: %s" , u . Name )
2015-08-18 23:58:45 +03:00
// Auto-set admin for the only user.
if models . CountUsers ( ) == 1 {
u . IsAdmin = true
u . IsActive = true
if err := models . UpdateUser ( u ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
}
2016-07-15 19:36:39 +03:00
// Send confirmation email, no need for social account.
2016-07-23 20:08:22 +03:00
if setting . Service . RegisterEmailConfirm && u . ID > 1 {
2016-07-15 19:36:39 +03:00
models . SendActivateAccountMail ( ctx . Context , u )
2014-08-10 04:25:02 +04:00
ctx . Data [ "IsSendRegisterMail" ] = true
ctx . Data [ "Email" ] = u . Email
2017-05-29 10:35:47 +03:00
ctx . Data [ "ActiveCodeLives" ] = base . MinutesToFriendly ( setting . Service . ActiveCodeLives )
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , TplActivate )
2014-08-10 04:25:02 +04:00
if err := ctx . Cache . Put ( "MailResendLimit_" + u . LowerName , u . LowerName , 180 ) ; err != nil {
log . Error ( 4 , "Set cache(MailResendLimit) fail: %v" , err )
}
return
}
2014-07-26 08:24:27 +04:00
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/login" )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// Activate render activate user page
2016-03-11 19:56:52 +03:00
func Activate ( ctx * context . Context ) {
2014-08-10 08:02:00 +04:00
code := ctx . Query ( "code" )
if len ( code ) == 0 {
ctx . Data [ "IsActivatePage" ] = true
if ctx . User . IsActive {
ctx . Error ( 404 )
return
}
2016-07-15 19:36:39 +03:00
// Resend confirmation email.
2014-08-10 08:02:00 +04:00
if setting . Service . RegisterEmailConfirm {
if ctx . Cache . IsExist ( "MailResendLimit_" + ctx . User . LowerName ) {
ctx . Data [ "ResendLimited" ] = true
} else {
2017-05-29 10:35:47 +03:00
ctx . Data [ "ActiveCodeLives" ] = base . MinutesToFriendly ( setting . Service . ActiveCodeLives )
2016-07-15 19:36:39 +03:00
models . SendActivateAccountMail ( ctx . Context , ctx . User )
2014-08-10 08:02:00 +04: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 )
}
}
} else {
ctx . Data [ "ServiceNotEnabled" ] = true
}
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , TplActivate )
2014-08-10 08:02:00 +04:00
return
}
// Verify code.
if user := models . VerifyUserActiveCode ( code ) ; user != nil {
user . IsActive = true
2016-12-20 15:32:02 +03:00
var err error
if user . Rands , err = models . GetUserSalt ( ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
2014-08-10 08:02:00 +04:00
if err := models . UpdateUser ( user ) ; err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2014-08-10 08:02:00 +04:00
ctx . Error ( 404 )
} else {
ctx . Handle ( 500 , "UpdateUser" , err )
}
return
}
log . Trace ( "User activated: %s" , user . Name )
2016-07-23 20:08:22 +03:00
ctx . Session . Set ( "uid" , user . ID )
2014-08-10 08:02:00 +04:00
ctx . Session . Set ( "uname" , user . Name )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/" )
2014-08-10 08:02:00 +04:00
return
}
ctx . Data [ "IsActivateFailed" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , TplActivate )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// ActivateEmail render the activate email page
2016-03-11 19:56:52 +03:00
func ActivateEmail ( ctx * context . Context ) {
2014-12-17 18:41:49 +03:00
code := ctx . Query ( "code" )
2016-11-18 06:03:03 +03:00
emailStr := ctx . Query ( "email" )
2014-12-17 18:41:49 +03:00
// Verify code.
2016-11-18 06:03:03 +03:00
if email := models . VerifyActiveEmailCode ( code , emailStr ) ; email != nil {
2015-02-22 06:13:47 +03:00
if err := email . Activate ( ) ; err != nil {
2014-12-17 18:41:49 +03:00
ctx . Handle ( 500 , "ActivateEmail" , err )
}
log . Trace ( "Email activated: %s" , email . Email )
2015-11-19 19:52:39 +03:00
ctx . Flash . Success ( ctx . Tr ( "settings.add_email_success" ) )
2014-12-17 18:41:49 +03:00
}
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/settings/email" )
2014-12-17 18:41:49 +03:00
return
}
2016-11-18 06:03:03 +03:00
// ForgotPasswd render the forget pasword page
2016-03-11 19:56:52 +03:00
func ForgotPasswd ( ctx * context . Context ) {
2017-02-20 21:27:20 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "auth.forgot_password_title" )
2014-07-26 08:24:27 +04:00
if setting . MailService == nil {
ctx . Data [ "IsResetDisable" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplForgotPassword )
2014-07-26 08:24:27 +04:00
return
}
2017-03-11 12:11:54 +03:00
email := ctx . Query ( "email" )
ctx . Data [ "Email" ] = email
2014-07-26 08:24:27 +04:00
ctx . Data [ "IsResetRequest" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplForgotPassword )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// ForgotPasswdPost response for forget password request
2016-03-11 19:56:52 +03:00
func ForgotPasswdPost ( ctx * context . Context ) {
2017-02-20 21:27:20 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "auth.forgot_password_title" )
2014-08-10 08:02:00 +04:00
if setting . MailService == nil {
2015-09-17 21:57:24 +03:00
ctx . Handle ( 403 , "ForgotPasswdPost" , nil )
2014-08-10 08:02:00 +04:00
return
}
ctx . Data [ "IsResetRequest" ] = true
email := ctx . Query ( "email" )
2015-09-17 21:57:24 +03:00
ctx . Data [ "Email" ] = email
2014-08-10 08:02:00 +04:00
u , err := models . GetUserByEmail ( email )
if err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2017-05-29 10:35:47 +03:00
ctx . Data [ "ResetPwdCodeLives" ] = base . MinutesToFriendly ( setting . Service . ResetPwdCodeLives )
2016-10-17 05:08:40 +03:00
ctx . Data [ "IsResetSent" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplForgotPassword )
2016-10-17 05:08:40 +03:00
return
2014-08-10 08:02:00 +04:00
}
2016-11-18 06:03:03 +03:00
ctx . Handle ( 500 , "user.ResetPasswd(check existence)" , err )
2014-08-10 08:02:00 +04:00
return
}
2017-02-22 10:14:37 +03:00
if ! u . IsLocal ( ) && ! u . IsOAuth2 ( ) {
2016-03-14 17:40:16 +03:00
ctx . Data [ "Err_Email" ] = true
2016-11-18 06:03:03 +03:00
ctx . RenderWithErr ( ctx . Tr ( "auth.non_local_account" ) , tplForgotPassword , nil )
2016-03-14 17:40:16 +03:00
return
}
2014-08-10 08:02:00 +04:00
if ctx . Cache . IsExist ( "MailResendLimit_" + u . LowerName ) {
ctx . Data [ "ResendLimited" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplForgotPassword )
2014-08-10 08:02:00 +04:00
return
}
2016-07-15 19:36:39 +03:00
models . SendResetPasswordMail ( ctx . Context , u )
2014-08-10 08:02:00 +04:00
if err = ctx . Cache . Put ( "MailResendLimit_" + u . LowerName , u . LowerName , 180 ) ; err != nil {
log . Error ( 4 , "Set cache(MailResendLimit) fail: %v" , err )
}
2017-05-29 10:35:47 +03:00
ctx . Data [ "ResetPwdCodeLives" ] = base . MinutesToFriendly ( setting . Service . ResetPwdCodeLives )
2014-08-10 08:02:00 +04:00
ctx . Data [ "IsResetSent" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplForgotPassword )
2014-07-26 08:24:27 +04:00
}
2016-11-18 06:03:03 +03:00
// ResetPasswd render the reset password page
2016-03-11 19:56:52 +03:00
func ResetPasswd ( ctx * context . Context ) {
2014-08-10 08:02:00 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "auth.reset_password" )
2014-07-26 08:24:27 +04:00
code := ctx . Query ( "code" )
if len ( code ) == 0 {
ctx . Error ( 404 )
return
}
ctx . Data [ "Code" ] = code
ctx . Data [ "IsResetForm" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplResetPassword )
2014-07-26 08:24:27 +04:00
}
2016-12-24 16:40:44 +03:00
// ResetPasswdPost response from reset password request
2016-03-11 19:56:52 +03:00
func ResetPasswdPost ( ctx * context . Context ) {
2014-08-10 08:02:00 +04:00
ctx . Data [ "Title" ] = ctx . Tr ( "auth.reset_password" )
code := ctx . Query ( "code" )
if len ( code ) == 0 {
ctx . Error ( 404 )
return
}
ctx . Data [ "Code" ] = code
if u := models . VerifyUserActiveCode ( code ) ; u != nil {
// Validate password length.
passwd := ctx . Query ( "password" )
2016-12-24 16:40:44 +03:00
if len ( passwd ) < setting . MinPasswordLength {
2014-08-10 08:02:00 +04:00
ctx . Data [ "IsResetForm" ] = true
ctx . Data [ "Err_Password" ] = true
2016-12-24 16:40:44 +03:00
ctx . RenderWithErr ( ctx . Tr ( "auth.password_too_short" , setting . MinPasswordLength ) , tplResetPassword , nil )
2014-08-10 08:02:00 +04:00
return
}
u . Passwd = passwd
2016-12-20 15:32:02 +03:00
var err error
if u . Rands , err = models . GetUserSalt ( ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
if u . Salt , err = models . GetUserSalt ( ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
2014-08-10 08:02:00 +04:00
u . EncodePasswd ( )
if err := models . UpdateUser ( u ) ; err != nil {
ctx . Handle ( 500 , "UpdateUser" , err )
return
}
log . Trace ( "User password reset: %s" , u . Name )
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/login" )
2014-08-10 08:02:00 +04:00
return
}
ctx . Data [ "IsResetFailed" ] = true
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplResetPassword )
2014-07-26 08:24:27 +04:00
}