code.gitea.io/gitea@v1.21.7/routers/web/auth/auth.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 auth
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"strings"
    12  
    13  	"code.gitea.io/gitea/models/auth"
    14  	"code.gitea.io/gitea/models/db"
    15  	user_model "code.gitea.io/gitea/models/user"
    16  	"code.gitea.io/gitea/modules/auth/password"
    17  	"code.gitea.io/gitea/modules/base"
    18  	"code.gitea.io/gitea/modules/context"
    19  	"code.gitea.io/gitea/modules/eventsource"
    20  	"code.gitea.io/gitea/modules/log"
    21  	"code.gitea.io/gitea/modules/session"
    22  	"code.gitea.io/gitea/modules/setting"
    23  	"code.gitea.io/gitea/modules/timeutil"
    24  	"code.gitea.io/gitea/modules/util"
    25  	"code.gitea.io/gitea/modules/web"
    26  	"code.gitea.io/gitea/modules/web/middleware"
    27  	"code.gitea.io/gitea/routers/utils"
    28  	auth_service "code.gitea.io/gitea/services/auth"
    29  	"code.gitea.io/gitea/services/auth/source/oauth2"
    30  	"code.gitea.io/gitea/services/externalaccount"
    31  	"code.gitea.io/gitea/services/forms"
    32  	"code.gitea.io/gitea/services/mailer"
    33  
    34  	"github.com/markbates/goth"
    35  )
    36  
    37  const (
    38  	// tplSignIn template for sign in page
    39  	tplSignIn base.TplName = "user/auth/signin"
    40  	// tplSignUp template path for sign up page
    41  	tplSignUp base.TplName = "user/auth/signup"
    42  	// TplActivate template path for activate user
    43  	TplActivate base.TplName = "user/auth/activate"
    44  )
    45  
    46  // AutoSignIn reads cookie and try to auto-login.
    47  func AutoSignIn(ctx *context.Context) (bool, error) {
    48  	uname := ctx.GetSiteCookie(setting.CookieUserName)
    49  	if len(uname) == 0 {
    50  		return false, nil
    51  	}
    52  
    53  	isSucceed := false
    54  	defer func() {
    55  		if !isSucceed {
    56  			log.Trace("auto-login cookie cleared: %s", uname)
    57  			ctx.DeleteSiteCookie(setting.CookieUserName)
    58  			ctx.DeleteSiteCookie(setting.CookieRememberName)
    59  		}
    60  	}()
    61  
    62  	u, err := user_model.GetUserByName(ctx, uname)
    63  	if err != nil {
    64  		if !user_model.IsErrUserNotExist(err) {
    65  			return false, fmt.Errorf("GetUserByName: %w", err)
    66  		}
    67  		return false, nil
    68  	}
    69  
    70  	if val, ok := ctx.GetSuperSecureCookie(
    71  		base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); !ok || val != u.Name {
    72  		return false, nil
    73  	}
    74  
    75  	isSucceed = true
    76  
    77  	if err := updateSession(ctx, nil, map[string]any{
    78  		// Set session IDs
    79  		"uid":   u.ID,
    80  		"uname": u.Name,
    81  	}); err != nil {
    82  		return false, fmt.Errorf("unable to updateSession: %w", err)
    83  	}
    84  
    85  	if err := resetLocale(ctx, u); err != nil {
    86  		return false, err
    87  	}
    88  
    89  	ctx.Csrf.DeleteCookie(ctx)
    90  	return true, nil
    91  }
    92  
    93  func resetLocale(ctx *context.Context, u *user_model.User) error {
    94  	// Language setting of the user overwrites the one previously set
    95  	// If the user does not have a locale set, we save the current one.
    96  	if len(u.Language) == 0 {
    97  		u.Language = ctx.Locale.Language()
    98  		if err := user_model.UpdateUserCols(ctx, u, "language"); err != nil {
    99  			return err
   100  		}
   101  	}
   102  
   103  	middleware.SetLocaleCookie(ctx.Resp, u.Language, 0)
   104  
   105  	if ctx.Locale.Language() != u.Language {
   106  		ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func checkAutoLogin(ctx *context.Context) bool {
   113  	// Check auto-login
   114  	isSucceed, err := AutoSignIn(ctx)
   115  	if err != nil {
   116  		ctx.ServerError("AutoSignIn", err)
   117  		return true
   118  	}
   119  
   120  	redirectTo := ctx.FormString("redirect_to")
   121  	if len(redirectTo) > 0 {
   122  		middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
   123  	} else {
   124  		redirectTo = ctx.GetSiteCookie("redirect_to")
   125  	}
   126  
   127  	if isSucceed {
   128  		middleware.DeleteRedirectToCookie(ctx.Resp)
   129  		nextRedirectTo := setting.AppSubURL + string(setting.LandingPageURL)
   130  		if setting.LandingPageURL == setting.LandingPageLogin {
   131  			nextRedirectTo = setting.AppSubURL + "/" // do not cycle-redirect to the login page
   132  		}
   133  		ctx.RedirectToFirst(redirectTo, nextRedirectTo)
   134  		return true
   135  	}
   136  
   137  	return false
   138  }
   139  
   140  // SignIn render sign in page
   141  func SignIn(ctx *context.Context) {
   142  	ctx.Data["Title"] = ctx.Tr("sign_in")
   143  
   144  	// Check auto-login
   145  	if checkAutoLogin(ctx) {
   146  		return
   147  	}
   148  
   149  	orderedOAuth2Names, oauth2Providers, err := oauth2.GetOAuth2ProvidersMap(true)
   150  	if err != nil {
   151  		ctx.ServerError("UserSignIn", err)
   152  		return
   153  	}
   154  	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
   155  	ctx.Data["OAuth2Providers"] = oauth2Providers
   156  	ctx.Data["Title"] = ctx.Tr("sign_in")
   157  	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
   158  	ctx.Data["PageIsSignIn"] = true
   159  	ctx.Data["PageIsLogin"] = true
   160  	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled()
   161  
   162  	if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
   163  		context.SetCaptchaData(ctx)
   164  	}
   165  
   166  	ctx.HTML(http.StatusOK, tplSignIn)
   167  }
   168  
   169  // SignInPost response for sign in request
   170  func SignInPost(ctx *context.Context) {
   171  	ctx.Data["Title"] = ctx.Tr("sign_in")
   172  
   173  	orderedOAuth2Names, oauth2Providers, err := oauth2.GetOAuth2ProvidersMap(true)
   174  	if err != nil {
   175  		ctx.ServerError("UserSignIn", err)
   176  		return
   177  	}
   178  	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
   179  	ctx.Data["OAuth2Providers"] = oauth2Providers
   180  	ctx.Data["Title"] = ctx.Tr("sign_in")
   181  	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
   182  	ctx.Data["PageIsSignIn"] = true
   183  	ctx.Data["PageIsLogin"] = true
   184  	ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled()
   185  
   186  	if ctx.HasError() {
   187  		ctx.HTML(http.StatusOK, tplSignIn)
   188  		return
   189  	}
   190  
   191  	form := web.GetForm(ctx).(*forms.SignInForm)
   192  
   193  	if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
   194  		context.SetCaptchaData(ctx)
   195  
   196  		context.VerifyCaptcha(ctx, tplSignIn, form)
   197  		if ctx.Written() {
   198  			return
   199  		}
   200  	}
   201  
   202  	u, source, err := auth_service.UserSignIn(ctx, form.UserName, form.Password)
   203  	if err != nil {
   204  		if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) {
   205  			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
   206  			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
   207  		} else if user_model.IsErrEmailAlreadyUsed(err) {
   208  			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignIn, &form)
   209  			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
   210  		} else if user_model.IsErrUserProhibitLogin(err) {
   211  			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
   212  			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
   213  			ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
   214  		} else if user_model.IsErrUserInactive(err) {
   215  			if setting.Service.RegisterEmailConfirm {
   216  				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
   217  				ctx.HTML(http.StatusOK, TplActivate)
   218  			} else {
   219  				log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
   220  				ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
   221  				ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
   222  			}
   223  		} else {
   224  			ctx.ServerError("UserSignIn", err)
   225  		}
   226  		return
   227  	}
   228  
   229  	// Now handle 2FA:
   230  
   231  	// First of all if the source can skip local two fa we're done
   232  	if skipper, ok := source.Cfg.(auth_service.LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() {
   233  		handleSignIn(ctx, u, form.Remember)
   234  		return
   235  	}
   236  
   237  	// If this user is enrolled in 2FA TOTP, we can't sign the user in just yet.
   238  	// Instead, redirect them to the 2FA authentication page.
   239  	hasTOTPtwofa, err := auth.HasTwoFactorByUID(ctx, u.ID)
   240  	if err != nil {
   241  		ctx.ServerError("UserSignIn", err)
   242  		return
   243  	}
   244  
   245  	// Check if the user has webauthn registration
   246  	hasWebAuthnTwofa, err := auth.HasWebAuthnRegistrationsByUID(ctx, u.ID)
   247  	if err != nil {
   248  		ctx.ServerError("UserSignIn", err)
   249  		return
   250  	}
   251  
   252  	if !hasTOTPtwofa && !hasWebAuthnTwofa {
   253  		// No two factor auth configured we can sign in the user
   254  		handleSignIn(ctx, u, form.Remember)
   255  		return
   256  	}
   257  
   258  	updates := map[string]any{
   259  		// User will need to use 2FA TOTP or WebAuthn, save data
   260  		"twofaUid":      u.ID,
   261  		"twofaRemember": form.Remember,
   262  	}
   263  	if hasTOTPtwofa {
   264  		// User will need to use WebAuthn, save data
   265  		updates["totpEnrolled"] = u.ID
   266  	}
   267  	if err := updateSession(ctx, nil, updates); err != nil {
   268  		ctx.ServerError("UserSignIn: Unable to update session", err)
   269  		return
   270  	}
   271  
   272  	// If we have WebAuthn redirect there first
   273  	if hasWebAuthnTwofa {
   274  		ctx.Redirect(setting.AppSubURL + "/user/webauthn")
   275  		return
   276  	}
   277  
   278  	// Fallback to 2FA
   279  	ctx.Redirect(setting.AppSubURL + "/user/two_factor")
   280  }
   281  
   282  // This handles the final part of the sign-in process of the user.
   283  func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) {
   284  	redirect := handleSignInFull(ctx, u, remember, true)
   285  	if ctx.Written() {
   286  		return
   287  	}
   288  	ctx.Redirect(redirect)
   289  }
   290  
   291  func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string {
   292  	if remember {
   293  		days := 86400 * setting.LogInRememberDays
   294  		ctx.SetSiteCookie(setting.CookieUserName, u.Name, days)
   295  		ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
   296  			setting.CookieRememberName, u.Name, days)
   297  	}
   298  
   299  	if err := updateSession(ctx, []string{
   300  		// Delete the openid, 2fa and linkaccount data
   301  		"openid_verified_uri",
   302  		"openid_signin_remember",
   303  		"openid_determined_email",
   304  		"openid_determined_username",
   305  		"twofaUid",
   306  		"twofaRemember",
   307  		"linkAccount",
   308  	}, map[string]any{
   309  		"uid":   u.ID,
   310  		"uname": u.Name,
   311  	}); err != nil {
   312  		ctx.ServerError("RegenerateSession", err)
   313  		return setting.AppSubURL + "/"
   314  	}
   315  
   316  	// Language setting of the user overwrites the one previously set
   317  	// If the user does not have a locale set, we save the current one.
   318  	if len(u.Language) == 0 {
   319  		u.Language = ctx.Locale.Language()
   320  		if err := user_model.UpdateUserCols(ctx, u, "language"); err != nil {
   321  			ctx.ServerError("UpdateUserCols Language", fmt.Errorf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
   322  			return setting.AppSubURL + "/"
   323  		}
   324  	}
   325  
   326  	middleware.SetLocaleCookie(ctx.Resp, u.Language, 0)
   327  
   328  	if ctx.Locale.Language() != u.Language {
   329  		ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
   330  	}
   331  
   332  	// Clear whatever CSRF cookie has right now, force to generate a new one
   333  	ctx.Csrf.DeleteCookie(ctx)
   334  
   335  	// Register last login
   336  	u.SetLastLogin()
   337  	if err := user_model.UpdateUserCols(ctx, u, "last_login_unix"); err != nil {
   338  		ctx.ServerError("UpdateUserCols", err)
   339  		return setting.AppSubURL + "/"
   340  	}
   341  
   342  	if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
   343  		middleware.DeleteRedirectToCookie(ctx.Resp)
   344  		if obeyRedirect {
   345  			ctx.RedirectToFirst(redirectTo)
   346  		}
   347  		return redirectTo
   348  	}
   349  
   350  	if obeyRedirect {
   351  		ctx.Redirect(setting.AppSubURL + "/")
   352  	}
   353  	return setting.AppSubURL + "/"
   354  }
   355  
   356  func getUserName(gothUser *goth.User) string {
   357  	switch setting.OAuth2Client.Username {
   358  	case setting.OAuth2UsernameEmail:
   359  		return strings.Split(gothUser.Email, "@")[0]
   360  	case setting.OAuth2UsernameNickname:
   361  		return gothUser.NickName
   362  	default: // OAuth2UsernameUserid
   363  		return gothUser.UserID
   364  	}
   365  }
   366  
   367  // HandleSignOut resets the session and sets the cookies
   368  func HandleSignOut(ctx *context.Context) {
   369  	_ = ctx.Session.Flush()
   370  	_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
   371  	ctx.DeleteSiteCookie(setting.CookieUserName)
   372  	ctx.DeleteSiteCookie(setting.CookieRememberName)
   373  	ctx.Csrf.DeleteCookie(ctx)
   374  	middleware.DeleteRedirectToCookie(ctx.Resp)
   375  }
   376  
   377  // SignOut sign out from login status
   378  func SignOut(ctx *context.Context) {
   379  	if ctx.Doer != nil {
   380  		eventsource.GetManager().SendMessageBlocking(ctx.Doer.ID, &eventsource.Event{
   381  			Name: "logout",
   382  			Data: ctx.Session.ID(),
   383  		})
   384  	}
   385  	HandleSignOut(ctx)
   386  	ctx.JSONRedirect(setting.AppSubURL + "/")
   387  }
   388  
   389  // SignUp render the register page
   390  func SignUp(ctx *context.Context) {
   391  	ctx.Data["Title"] = ctx.Tr("sign_up")
   392  
   393  	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
   394  
   395  	orderedOAuth2Names, oauth2Providers, err := oauth2.GetOAuth2ProvidersMap(true)
   396  	if err != nil {
   397  		ctx.ServerError("UserSignUp", err)
   398  		return
   399  	}
   400  
   401  	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
   402  	ctx.Data["OAuth2Providers"] = oauth2Providers
   403  	context.SetCaptchaData(ctx)
   404  
   405  	ctx.Data["PageIsSignUp"] = true
   406  
   407  	// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
   408  	ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration
   409  
   410  	redirectTo := ctx.FormString("redirect_to")
   411  	if len(redirectTo) > 0 {
   412  		middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
   413  	}
   414  
   415  	ctx.HTML(http.StatusOK, tplSignUp)
   416  }
   417  
   418  // SignUpPost response for sign up information submission
   419  func SignUpPost(ctx *context.Context) {
   420  	form := web.GetForm(ctx).(*forms.RegisterForm)
   421  	ctx.Data["Title"] = ctx.Tr("sign_up")
   422  
   423  	ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
   424  
   425  	orderedOAuth2Names, oauth2Providers, err := oauth2.GetOAuth2ProvidersMap(true)
   426  	if err != nil {
   427  		ctx.ServerError("UserSignUp", err)
   428  		return
   429  	}
   430  
   431  	ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
   432  	ctx.Data["OAuth2Providers"] = oauth2Providers
   433  	context.SetCaptchaData(ctx)
   434  
   435  	ctx.Data["PageIsSignUp"] = true
   436  
   437  	// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
   438  	if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
   439  		ctx.Error(http.StatusForbidden)
   440  		return
   441  	}
   442  
   443  	if ctx.HasError() {
   444  		ctx.HTML(http.StatusOK, tplSignUp)
   445  		return
   446  	}
   447  
   448  	context.VerifyCaptcha(ctx, tplSignUp, form)
   449  	if ctx.Written() {
   450  		return
   451  	}
   452  
   453  	if !form.IsEmailDomainAllowed() {
   454  		ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form)
   455  		return
   456  	}
   457  
   458  	if form.Password != form.Retype {
   459  		ctx.Data["Err_Password"] = true
   460  		ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplSignUp, &form)
   461  		return
   462  	}
   463  	if len(form.Password) < setting.MinPasswordLength {
   464  		ctx.Data["Err_Password"] = true
   465  		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplSignUp, &form)
   466  		return
   467  	}
   468  	if !password.IsComplexEnough(form.Password) {
   469  		ctx.Data["Err_Password"] = true
   470  		ctx.RenderWithErr(password.BuildComplexityError(ctx.Locale), tplSignUp, &form)
   471  		return
   472  	}
   473  	pwned, err := password.IsPwned(ctx, form.Password)
   474  	if pwned {
   475  		errMsg := ctx.Tr("auth.password_pwned")
   476  		if err != nil {
   477  			log.Error(err.Error())
   478  			errMsg = ctx.Tr("auth.password_pwned_err")
   479  		}
   480  		ctx.Data["Err_Password"] = true
   481  		ctx.RenderWithErr(errMsg, tplSignUp, &form)
   482  		return
   483  	}
   484  
   485  	u := &user_model.User{
   486  		Name:   form.UserName,
   487  		Email:  form.Email,
   488  		Passwd: form.Password,
   489  	}
   490  
   491  	if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false) {
   492  		// error already handled
   493  		return
   494  	}
   495  
   496  	ctx.Flash.Success(ctx.Tr("auth.sign_up_successful"))
   497  	handleSignIn(ctx, u, false)
   498  }
   499  
   500  // createAndHandleCreatedUser calls createUserInContext and
   501  // then handleUserCreated.
   502  func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool {
   503  	if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) {
   504  		return false
   505  	}
   506  	return handleUserCreated(ctx, u, gothUser)
   507  }
   508  
   509  // createUserInContext creates a user and handles errors within a given context.
   510  // Optionally a template can be specified.
   511  func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) {
   512  	if err := user_model.CreateUser(ctx, u, overwrites); err != nil {
   513  		if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
   514  			if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
   515  				var user *user_model.User
   516  				user = &user_model.User{Name: u.Name}
   517  				hasUser, err := user_model.GetUser(ctx, user)
   518  				if !hasUser || err != nil {
   519  					user = &user_model.User{Email: u.Email}
   520  					hasUser, err = user_model.GetUser(ctx, user)
   521  					if !hasUser || err != nil {
   522  						ctx.ServerError("UserLinkAccount", err)
   523  						return false
   524  					}
   525  				}
   526  
   527  				// TODO: probably we should respect 'remember' user's choice...
   528  				linkAccount(ctx, user, *gothUser, true)
   529  				return false // user is already created here, all redirects are handled
   530  			} else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin {
   531  				showLinkingLogin(ctx, *gothUser)
   532  				return false // user will be created only after linking login
   533  			}
   534  		}
   535  
   536  		// handle error without template
   537  		if len(tpl) == 0 {
   538  			ctx.ServerError("CreateUser", err)
   539  			return false
   540  		}
   541  
   542  		// handle error with template
   543  		switch {
   544  		case user_model.IsErrUserAlreadyExist(err):
   545  			ctx.Data["Err_UserName"] = true
   546  			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tpl, form)
   547  		case user_model.IsErrEmailAlreadyUsed(err):
   548  			ctx.Data["Err_Email"] = true
   549  			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form)
   550  		case user_model.IsErrEmailCharIsNotSupported(err):
   551  			ctx.Data["Err_Email"] = true
   552  			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
   553  		case user_model.IsErrEmailInvalid(err):
   554  			ctx.Data["Err_Email"] = true
   555  			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
   556  		case db.IsErrNameReserved(err):
   557  			ctx.Data["Err_UserName"] = true
   558  			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tpl, form)
   559  		case db.IsErrNamePatternNotAllowed(err):
   560  			ctx.Data["Err_UserName"] = true
   561  			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tpl, form)
   562  		case db.IsErrNameCharsNotAllowed(err):
   563  			ctx.Data["Err_UserName"] = true
   564  			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tpl, form)
   565  		default:
   566  			ctx.ServerError("CreateUser", err)
   567  		}
   568  		return false
   569  	}
   570  	log.Trace("Account created: %s", u.Name)
   571  	return true
   572  }
   573  
   574  // handleUserCreated does additional steps after a new user is created.
   575  // It auto-sets admin for the only user, updates the optional external user and
   576  // sends a confirmation email if required.
   577  func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) {
   578  	// Auto-set admin for the only user.
   579  	if user_model.CountUsers(ctx, nil) == 1 {
   580  		u.IsAdmin = true
   581  		u.IsActive = true
   582  		u.SetLastLogin()
   583  		if err := user_model.UpdateUserCols(ctx, u, "is_admin", "is_active", "last_login_unix"); err != nil {
   584  			ctx.ServerError("UpdateUser", err)
   585  			return false
   586  		}
   587  	}
   588  
   589  	// update external user information
   590  	if gothUser != nil {
   591  		if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
   592  			if !errors.Is(err, util.ErrNotExist) {
   593  				log.Error("UpdateExternalUser failed: %v", err)
   594  			}
   595  		}
   596  	}
   597  
   598  	// Send confirmation email
   599  	if !u.IsActive && u.ID > 1 {
   600  		if setting.Service.RegisterManualConfirm {
   601  			ctx.Data["ManualActivationOnly"] = true
   602  			ctx.HTML(http.StatusOK, TplActivate)
   603  			return false
   604  		}
   605  
   606  		mailer.SendActivateAccountMail(ctx.Locale, u)
   607  
   608  		ctx.Data["IsSendRegisterMail"] = true
   609  		ctx.Data["Email"] = u.Email
   610  		ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
   611  		ctx.HTML(http.StatusOK, TplActivate)
   612  
   613  		if setting.CacheService.Enabled {
   614  			if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
   615  				log.Error("Set cache(MailResendLimit) fail: %v", err)
   616  			}
   617  		}
   618  		return false
   619  	}
   620  
   621  	return true
   622  }
   623  
   624  // Activate render activate user page
   625  func Activate(ctx *context.Context) {
   626  	code := ctx.FormString("code")
   627  
   628  	if len(code) == 0 {
   629  		ctx.Data["IsActivatePage"] = true
   630  		if ctx.Doer == nil || ctx.Doer.IsActive {
   631  			ctx.NotFound("invalid user", nil)
   632  			return
   633  		}
   634  		// Resend confirmation email.
   635  		if setting.Service.RegisterEmailConfirm {
   636  			if setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.Doer.LowerName) {
   637  				ctx.Data["ResendLimited"] = true
   638  			} else {
   639  				ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
   640  				mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer)
   641  
   642  				if setting.CacheService.Enabled {
   643  					if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
   644  						log.Error("Set cache(MailResendLimit) fail: %v", err)
   645  					}
   646  				}
   647  			}
   648  		} else {
   649  			ctx.Data["ServiceNotEnabled"] = true
   650  		}
   651  		ctx.HTML(http.StatusOK, TplActivate)
   652  		return
   653  	}
   654  
   655  	user := user_model.VerifyUserActiveCode(ctx, code)
   656  	// if code is wrong
   657  	if user == nil {
   658  		ctx.Data["IsCodeInvalid"] = true
   659  		ctx.HTML(http.StatusOK, TplActivate)
   660  		return
   661  	}
   662  
   663  	// if account is local account, verify password
   664  	if user.LoginSource == 0 {
   665  		ctx.Data["Code"] = code
   666  		ctx.Data["NeedsPassword"] = true
   667  		ctx.HTML(http.StatusOK, TplActivate)
   668  		return
   669  	}
   670  
   671  	handleAccountActivation(ctx, user)
   672  }
   673  
   674  // ActivatePost handles account activation with password check
   675  func ActivatePost(ctx *context.Context) {
   676  	code := ctx.FormString("code")
   677  	if len(code) == 0 {
   678  		ctx.Redirect(setting.AppSubURL + "/user/activate")
   679  		return
   680  	}
   681  
   682  	user := user_model.VerifyUserActiveCode(ctx, code)
   683  	// if code is wrong
   684  	if user == nil {
   685  		ctx.Data["IsCodeInvalid"] = true
   686  		ctx.HTML(http.StatusOK, TplActivate)
   687  		return
   688  	}
   689  
   690  	// if account is local account, verify password
   691  	if user.LoginSource == 0 {
   692  		password := ctx.FormString("password")
   693  		if len(password) == 0 {
   694  			ctx.Data["Code"] = code
   695  			ctx.Data["NeedsPassword"] = true
   696  			ctx.HTML(http.StatusOK, TplActivate)
   697  			return
   698  		}
   699  		if !user.ValidatePassword(password) {
   700  			ctx.Data["IsPasswordInvalid"] = true
   701  			ctx.HTML(http.StatusOK, TplActivate)
   702  			return
   703  		}
   704  	}
   705  
   706  	handleAccountActivation(ctx, user)
   707  }
   708  
   709  func handleAccountActivation(ctx *context.Context, user *user_model.User) {
   710  	user.IsActive = true
   711  	var err error
   712  	if user.Rands, err = user_model.GetUserSalt(); err != nil {
   713  		ctx.ServerError("UpdateUser", err)
   714  		return
   715  	}
   716  	if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
   717  		if user_model.IsErrUserNotExist(err) {
   718  			ctx.NotFound("UpdateUserCols", err)
   719  		} else {
   720  			ctx.ServerError("UpdateUser", err)
   721  		}
   722  		return
   723  	}
   724  
   725  	if err := user_model.ActivateUserEmail(ctx, user.ID, user.Email, true); err != nil {
   726  		log.Error("Unable to activate email for user: %-v with email: %s: %v", user, user.Email, err)
   727  		ctx.ServerError("ActivateUserEmail", err)
   728  		return
   729  	}
   730  
   731  	log.Trace("User activated: %s", user.Name)
   732  
   733  	if err := updateSession(ctx, nil, map[string]any{
   734  		"uid":   user.ID,
   735  		"uname": user.Name,
   736  	}); err != nil {
   737  		log.Error("Unable to regenerate session for user: %-v with email: %s: %v", user, user.Email, err)
   738  		ctx.ServerError("ActivateUserEmail", err)
   739  		return
   740  	}
   741  
   742  	if err := resetLocale(ctx, user); err != nil {
   743  		ctx.ServerError("resetLocale", err)
   744  		return
   745  	}
   746  
   747  	// Register last login
   748  	user.SetLastLogin()
   749  	if err := user_model.UpdateUserCols(ctx, user, "last_login_unix"); err != nil {
   750  		ctx.ServerError("UpdateUserCols", err)
   751  		return
   752  	}
   753  
   754  	ctx.Flash.Success(ctx.Tr("auth.account_activated"))
   755  	if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 {
   756  		middleware.DeleteRedirectToCookie(ctx.Resp)
   757  		ctx.RedirectToFirst(redirectTo)
   758  		return
   759  	}
   760  
   761  	ctx.Redirect(setting.AppSubURL + "/")
   762  }
   763  
   764  // ActivateEmail render the activate email page
   765  func ActivateEmail(ctx *context.Context) {
   766  	code := ctx.FormString("code")
   767  	emailStr := ctx.FormString("email")
   768  
   769  	// Verify code.
   770  	if email := user_model.VerifyActiveEmailCode(ctx, code, emailStr); email != nil {
   771  		if err := user_model.ActivateEmail(ctx, email); err != nil {
   772  			ctx.ServerError("ActivateEmail", err)
   773  		}
   774  
   775  		log.Trace("Email activated: %s", email.Email)
   776  		ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
   777  
   778  		if u, err := user_model.GetUserByID(ctx, email.UID); err != nil {
   779  			log.Warn("GetUserByID: %d", email.UID)
   780  		} else if setting.CacheService.Enabled {
   781  			// Allow user to validate more emails
   782  			_ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName)
   783  		}
   784  	}
   785  
   786  	// FIXME: e-mail verification does not require the user to be logged in,
   787  	// so this could be redirecting to the login page.
   788  	// Should users be logged in automatically here? (consider 2FA requirements, etc.)
   789  	ctx.Redirect(setting.AppSubURL + "/user/settings/account")
   790  }
   791  
   792  func updateSession(ctx *context.Context, deletes []string, updates map[string]any) error {
   793  	if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
   794  		return fmt.Errorf("regenerate session: %w", err)
   795  	}
   796  	sess := ctx.Session
   797  	sessID := sess.ID()
   798  	for _, k := range deletes {
   799  		if err := sess.Delete(k); err != nil {
   800  			return fmt.Errorf("delete %v in session[%s]: %w", k, sessID, err)
   801  		}
   802  	}
   803  	for k, v := range updates {
   804  		if err := sess.Set(k, v); err != nil {
   805  			return fmt.Errorf("set %v in session[%s]: %w", k, sessID, err)
   806  		}
   807  	}
   808  	if err := sess.Release(); err != nil {
   809  		return fmt.Errorf("store session[%s]: %w", sessID, err)
   810  	}
   811  	return nil
   812  }