gitea源码

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