loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Enhanced auth token / remember me (#27606)

Closes #27455

> The mechanism responsible for long-term authentication (the 'remember
me' cookie) uses a weak construction technique. It will hash the user's
hashed password and the rands value; it will then call the secure cookie
code, which will encrypt the user's name with the computed hash. If one
were able to dump the database, they could extract those two values to
rebuild that cookie and impersonate a user. That vulnerability exists
from the date the dump was obtained until a user changed their password.
>
> To fix this security issue, the cookie could be created and verified
using a different technique such as the one explained at
https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#secure-remember-me-cookies.

The PR removes the now obsolete setting `COOKIE_USERNAME`.

authored by

KN4CK3R and committed by
GitHub
c6c829fe ee6a3906

+418 -103
-1
docs/content/administration/config-cheat-sheet.en-us.md
··· 517 517 - `SECRET_KEY`: **\<random at every install\>**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. 518 518 - `SECRET_KEY_URI`: **_empty_**: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_key`). It shouldn't be lost like SECRET_KEY. 519 519 - `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days. 520 - - `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username. 521 520 - `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication 522 521 information. 523 522 - `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**: Header name for reverse proxy
-1
docs/content/administration/config-cheat-sheet.zh-cn.md
··· 506 506 - `SECRET_KEY`: **\<每次安装时随机生成\>**:全局服务器安全密钥。这个密钥非常重要,如果丢失将无法解密加密的数据(例如 2FA)。 507 507 - `SECRET_KEY_URI`: **_empty_**:与定义 `SECRET_KEY` 不同,此选项可用于使用存储在文件中的密钥(示例值:`file:/etc/gitea/secret_key`)。它不应该像 `SECRET_KEY` 一样容易丢失。 508 508 - `LOGIN_REMEMBER_DAYS`: **7**:Cookie 保存时间,单位为天。 509 - - `COOKIE_USERNAME`: **gitea\_awesome**:保存用户名的 Cookie 名称。 510 509 - `COOKIE_REMEMBER_NAME`: **gitea\_incredible**:保存自动登录信息的 Cookie 名称。 511 510 - `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**:反向代理认证的 HTTP 头部名称,用于提供用户信息。 512 511 - `REVERSE_PROXY_AUTHENTICATION_EMAIL`: **X-WEBAUTH-EMAIL**:反向代理认证的 HTTP 头部名称,用于提供邮箱信息。
+60
models/auth/auth_token.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package auth 5 + 6 + import ( 7 + "context" 8 + 9 + "code.gitea.io/gitea/models/db" 10 + "code.gitea.io/gitea/modules/timeutil" 11 + "code.gitea.io/gitea/modules/util" 12 + 13 + "xorm.io/builder" 14 + ) 15 + 16 + var ErrAuthTokenNotExist = util.NewNotExistErrorf("auth token does not exist") 17 + 18 + type AuthToken struct { //nolint:revive 19 + ID string `xorm:"pk"` 20 + TokenHash string 21 + UserID int64 `xorm:"INDEX"` 22 + ExpiresUnix timeutil.TimeStamp `xorm:"INDEX"` 23 + } 24 + 25 + func init() { 26 + db.RegisterModel(new(AuthToken)) 27 + } 28 + 29 + func InsertAuthToken(ctx context.Context, t *AuthToken) error { 30 + _, err := db.GetEngine(ctx).Insert(t) 31 + return err 32 + } 33 + 34 + func GetAuthTokenByID(ctx context.Context, id string) (*AuthToken, error) { 35 + at := &AuthToken{} 36 + 37 + has, err := db.GetEngine(ctx).ID(id).Get(at) 38 + if err != nil { 39 + return nil, err 40 + } 41 + if !has { 42 + return nil, ErrAuthTokenNotExist 43 + } 44 + return at, nil 45 + } 46 + 47 + func UpdateAuthTokenByID(ctx context.Context, t *AuthToken) error { 48 + _, err := db.GetEngine(ctx).ID(t.ID).Cols("token_hash", "expires_unix").Update(t) 49 + return err 50 + } 51 + 52 + func DeleteAuthTokenByID(ctx context.Context, id string) error { 53 + _, err := db.GetEngine(ctx).ID(id).Delete(&AuthToken{}) 54 + return err 55 + } 56 + 57 + func DeleteExpiredAuthTokens(ctx context.Context) error { 58 + _, err := db.GetEngine(ctx).Where(builder.Lt{"expires_unix": timeutil.TimeStampNow()}).Delete(&AuthToken{}) 59 + return err 60 + }
models/auth/token.go models/auth/access_token.go
models/auth/token_scope.go models/auth/access_token_scope.go
models/auth/token_scope_test.go models/auth/access_token_scope_test.go
models/auth/token_test.go models/auth/access_token_test.go
+2
models/migrations/migrations.go
··· 546 546 547 547 // v280 -> v281 548 548 NewMigration("Rename user themes", v1_22.RenameUserThemes), 549 + // v281 -> v282 550 + NewMigration("Add auth_token table", v1_22.CreateAuthTokenTable), 549 551 } 550 552 551 553 // GetCurrentDBVersion returns the current db version
+21
models/migrations/v1_22/v281.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package v1_22 //nolint 5 + 6 + import ( 7 + "code.gitea.io/gitea/modules/timeutil" 8 + 9 + "xorm.io/xorm" 10 + ) 11 + 12 + func CreateAuthTokenTable(x *xorm.Engine) error { 13 + type AuthToken struct { 14 + ID string `xorm:"pk"` 15 + TokenHash string 16 + UserID int64 `xorm:"INDEX"` 17 + ExpiresUnix timeutil.TimeStamp `xorm:"INDEX"` 18 + } 19 + 20 + return x.Sync(new(AuthToken)) 21 + }
-44
modules/context/context_cookie.go
··· 4 4 package context 5 5 6 6 import ( 7 - "encoding/hex" 8 7 "net/http" 9 8 "strings" 10 9 11 10 "code.gitea.io/gitea/modules/setting" 12 - "code.gitea.io/gitea/modules/util" 13 11 "code.gitea.io/gitea/modules/web/middleware" 14 - 15 - "github.com/minio/sha256-simd" 16 - "golang.org/x/crypto/pbkdf2" 17 12 ) 18 13 19 14 const CookieNameFlash = "gitea_flash" ··· 45 40 func (ctx *Context) GetSiteCookie(name string) string { 46 41 return middleware.GetSiteCookie(ctx.Req, name) 47 42 } 48 - 49 - // GetSuperSecureCookie returns given cookie value from request header with secret string. 50 - func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) { 51 - val := ctx.GetSiteCookie(name) 52 - return ctx.CookieDecrypt(secret, val) 53 - } 54 - 55 - // CookieDecrypt returns given value from with secret string. 56 - func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) { 57 - if val == "" { 58 - return "", false 59 - } 60 - 61 - text, err := hex.DecodeString(val) 62 - if err != nil { 63 - return "", false 64 - } 65 - 66 - key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) 67 - text, err = util.AESGCMDecrypt(key, text) 68 - return string(text), err == nil 69 - } 70 - 71 - // SetSuperSecureCookie sets given cookie value to response header with secret string. 72 - func (ctx *Context) SetSuperSecureCookie(secret, name, value string, maxAge int) { 73 - text := ctx.CookieEncrypt(secret, value) 74 - ctx.SetSiteCookie(name, text, maxAge) 75 - } 76 - 77 - // CookieEncrypt encrypts a given value using the provided secret 78 - func (ctx *Context) CookieEncrypt(secret, value string) string { 79 - key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) 80 - text, err := util.AESGCMEncrypt(key, []byte(value)) 81 - if err != nil { 82 - panic("error encrypting cookie: " + err.Error()) 83 - } 84 - 85 - return hex.EncodeToString(text) 86 - }
-2
modules/setting/security.go
··· 19 19 SecretKey string 20 20 InternalToken string // internal access token 21 21 LogInRememberDays int 22 - CookieUserName string 23 22 CookieRememberName string 24 23 ReverseProxyAuthUser string 25 24 ReverseProxyAuthEmail string ··· 104 103 sec := rootCfg.Section("security") 105 104 InstallLock = HasInstallLock(rootCfg) 106 105 LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) 107 - CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") 108 106 SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY") 109 107 if SecretKey == "" { 110 108 // FIXME: https://github.com/go-gitea/gitea/issues/16832
+1
options/locale/locale_en-US.ini
··· 363 363 disable_register_mail = Email confirmation for registration is disabled. 364 364 manual_activation_only = Contact your site administrator to complete activation. 365 365 remember_me = Remember This Device 366 + remember_me.compromised = The login token is not valid anymore which may indicate a compromised account. Please check your account for unusual activities. 366 367 forgot_password_title= Forgot Password 367 368 forgot_password = Forgot password? 368 369 sign_up_now = Need an account? Register now.
+8 -4
routers/install/install.go
··· 27 27 "code.gitea.io/gitea/modules/log" 28 28 "code.gitea.io/gitea/modules/setting" 29 29 "code.gitea.io/gitea/modules/templates" 30 + "code.gitea.io/gitea/modules/timeutil" 30 31 "code.gitea.io/gitea/modules/translation" 31 32 "code.gitea.io/gitea/modules/user" 32 33 "code.gitea.io/gitea/modules/util" 33 34 "code.gitea.io/gitea/modules/web" 34 35 "code.gitea.io/gitea/modules/web/middleware" 35 36 "code.gitea.io/gitea/routers/common" 37 + auth_service "code.gitea.io/gitea/services/auth" 36 38 "code.gitea.io/gitea/services/forms" 37 39 38 40 "gitea.com/go-chi/session" ··· 547 549 u, _ = user_model.GetUserByName(ctx, u.Name) 548 550 } 549 551 550 - days := 86400 * setting.LogInRememberDays 551 - ctx.SetSiteCookie(setting.CookieUserName, u.Name, days) 552 + nt, token, err := auth_service.CreateAuthTokenForUserID(ctx, u.ID) 553 + if err != nil { 554 + ctx.ServerError("CreateAuthTokenForUserID", err) 555 + return 556 + } 552 557 553 - ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd), 554 - setting.CookieRememberName, u.Name, days) 558 + ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day) 555 559 556 560 // Auto-login for admin 557 561 if err = ctx.Session.Set("uid", u.ID); err != nil {
+2 -4
routers/web/auth/2fa.go
··· 26 26 func TwoFactor(ctx *context.Context) { 27 27 ctx.Data["Title"] = ctx.Tr("twofa") 28 28 29 - // Check auto-login. 30 - if checkAutoLogin(ctx) { 29 + if CheckAutoLogin(ctx) { 31 30 return 32 31 } 33 32 ··· 99 98 func TwoFactorScratch(ctx *context.Context) { 100 99 ctx.Data["Title"] = ctx.Tr("twofa_scratch") 101 100 102 - // Check auto-login. 103 - if checkAutoLogin(ctx) { 101 + if CheckAutoLogin(ctx) { 104 102 return 105 103 } 106 104
+40 -24
routers/web/auth/auth.go
··· 43 43 TplActivate base.TplName = "user/auth/activate" 44 44 ) 45 45 46 - // AutoSignIn reads cookie and try to auto-login. 47 - func AutoSignIn(ctx *context.Context) (bool, error) { 46 + // autoSignIn reads cookie and try to auto-login. 47 + func autoSignIn(ctx *context.Context) (bool, error) { 48 48 if !db.HasEngine { 49 49 return false, nil 50 50 } 51 51 52 - uname := ctx.GetSiteCookie(setting.CookieUserName) 53 - if len(uname) == 0 { 54 - return false, nil 55 - } 56 - 57 52 isSucceed := false 58 53 defer func() { 59 54 if !isSucceed { 60 - log.Trace("auto-login cookie cleared: %s", uname) 61 - ctx.DeleteSiteCookie(setting.CookieUserName) 62 55 ctx.DeleteSiteCookie(setting.CookieRememberName) 63 56 } 64 57 }() 65 58 66 - u, err := user_model.GetUserByName(ctx, uname) 59 + if err := auth.DeleteExpiredAuthTokens(ctx); err != nil { 60 + log.Error("Failed to delete expired auth tokens: %v", err) 61 + } 62 + 63 + t, err := auth_service.CheckAuthToken(ctx, ctx.GetSiteCookie(setting.CookieRememberName)) 67 64 if err != nil { 68 - if !user_model.IsErrUserNotExist(err) { 69 - return false, fmt.Errorf("GetUserByName: %w", err) 65 + switch err { 66 + case auth_service.ErrAuthTokenInvalidFormat, auth_service.ErrAuthTokenExpired: 67 + return false, nil 70 68 } 69 + return false, err 70 + } 71 + if t == nil { 71 72 return false, nil 72 73 } 73 74 74 - if val, ok := ctx.GetSuperSecureCookie( 75 - base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); !ok || val != u.Name { 75 + u, err := user_model.GetUserByID(ctx, t.UserID) 76 + if err != nil { 77 + if !user_model.IsErrUserNotExist(err) { 78 + return false, fmt.Errorf("GetUserByID: %w", err) 79 + } 76 80 return false, nil 77 81 } 78 82 79 83 isSucceed = true 80 84 85 + nt, token, err := auth_service.RegenerateAuthToken(ctx, t) 86 + if err != nil { 87 + return false, err 88 + } 89 + 90 + ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day) 91 + 81 92 if err := updateSession(ctx, nil, map[string]any{ 82 93 // Set session IDs 83 94 "uid": u.ID, ··· 113 124 return nil 114 125 } 115 126 116 - func checkAutoLogin(ctx *context.Context) bool { 127 + func CheckAutoLogin(ctx *context.Context) bool { 117 128 // Check auto-login 118 - isSucceed, err := AutoSignIn(ctx) 129 + isSucceed, err := autoSignIn(ctx) 119 130 if err != nil { 120 - ctx.ServerError("AutoSignIn", err) 131 + if errors.Is(err, auth_service.ErrAuthTokenInvalidHash) { 132 + ctx.Flash.Error(ctx.Tr("auth.remember_me.compromised"), true) 133 + return false 134 + } 135 + ctx.ServerError("autoSignIn", err) 121 136 return true 122 137 } 123 138 ··· 141 156 func SignIn(ctx *context.Context) { 142 157 ctx.Data["Title"] = ctx.Tr("sign_in") 143 158 144 - // Check auto-login 145 - if checkAutoLogin(ctx) { 159 + if CheckAutoLogin(ctx) { 146 160 return 147 161 } 148 162 ··· 290 304 291 305 func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string { 292 306 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) 307 + nt, token, err := auth_service.CreateAuthTokenForUserID(ctx, u.ID) 308 + if err != nil { 309 + ctx.ServerError("CreateAuthTokenForUserID", err) 310 + return setting.AppSubURL + "/" 311 + } 312 + 313 + ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day) 297 314 } 298 315 299 316 if err := updateSession(ctx, []string{ ··· 368 385 func HandleSignOut(ctx *context.Context) { 369 386 _ = ctx.Session.Flush() 370 387 _ = ctx.Session.Destroy(ctx.Resp, ctx.Req) 371 - ctx.DeleteSiteCookie(setting.CookieUserName) 372 388 ctx.DeleteSiteCookie(setting.CookieRememberName) 373 389 ctx.Csrf.DeleteCookie(ctx) 374 390 middleware.DeleteRedirectToCookie(ctx.Resp)
+1 -18
routers/web/auth/openid.go
··· 16 16 "code.gitea.io/gitea/modules/setting" 17 17 "code.gitea.io/gitea/modules/util" 18 18 "code.gitea.io/gitea/modules/web" 19 - "code.gitea.io/gitea/modules/web/middleware" 20 19 "code.gitea.io/gitea/services/auth" 21 20 "code.gitea.io/gitea/services/forms" 22 21 ) ··· 36 35 return 37 36 } 38 37 39 - // Check auto-login. 40 - isSucceed, err := AutoSignIn(ctx) 41 - if err != nil { 42 - ctx.ServerError("AutoSignIn", err) 43 - return 44 - } 45 - 46 - redirectTo := ctx.FormString("redirect_to") 47 - if len(redirectTo) > 0 { 48 - middleware.SetRedirectToCookie(ctx.Resp, redirectTo) 49 - } else { 50 - redirectTo = ctx.GetSiteCookie("redirect_to") 51 - } 52 - 53 - if isSucceed { 54 - middleware.DeleteRedirectToCookie(ctx.Resp) 55 - ctx.RedirectToFirst(redirectTo) 38 + if CheckAutoLogin(ctx) { 56 39 return 57 40 } 58 41
+1 -2
routers/web/auth/webauthn.go
··· 26 26 func WebAuthn(ctx *context.Context) { 27 27 ctx.Data["Title"] = ctx.Tr("twofa") 28 28 29 - // Check auto-login. 30 - if checkAutoLogin(ctx) { 29 + if CheckAutoLogin(ctx) { 31 30 return 32 31 } 33 32
+1 -2
routers/web/home.go
··· 54 54 } 55 55 56 56 // Check auto-login. 57 - uname := ctx.GetSiteCookie(setting.CookieUserName) 58 - if len(uname) != 0 { 57 + if ctx.GetSiteCookie(setting.CookieRememberName) != "" { 59 58 ctx.Redirect(setting.AppSubURL + "/user/login") 60 59 return 61 60 }
+1 -1
routers/web/web.go
··· 187 187 188 188 // Redirect to log in page if auto-signin info is provided and has not signed in. 189 189 if !options.SignOutRequired && !ctx.IsSigned && 190 - len(ctx.GetSiteCookie(setting.CookieUserName)) > 0 { 190 + ctx.GetSiteCookie(setting.CookieRememberName) != "" { 191 191 if ctx.Req.URL.Path != "/user/events" { 192 192 middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) 193 193 }
+123
services/auth/auth_token.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package auth 5 + 6 + import ( 7 + "context" 8 + "crypto/sha256" 9 + "crypto/subtle" 10 + "encoding/hex" 11 + "errors" 12 + "strings" 13 + "time" 14 + 15 + auth_model "code.gitea.io/gitea/models/auth" 16 + "code.gitea.io/gitea/modules/setting" 17 + "code.gitea.io/gitea/modules/timeutil" 18 + "code.gitea.io/gitea/modules/util" 19 + ) 20 + 21 + // Based on https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#secure-remember-me-cookies 22 + 23 + // The auth token consists of two parts: ID and token hash 24 + // Every device login creates a new auth token with an individual id and hash. 25 + // If a device uses the token to login into the instance, a fresh token gets generated which has the same id but a new hash. 26 + 27 + var ( 28 + ErrAuthTokenInvalidFormat = util.NewInvalidArgumentErrorf("auth token has an invalid format") 29 + ErrAuthTokenExpired = util.NewInvalidArgumentErrorf("auth token has expired") 30 + ErrAuthTokenInvalidHash = util.NewInvalidArgumentErrorf("auth token is invalid") 31 + ) 32 + 33 + func CheckAuthToken(ctx context.Context, value string) (*auth_model.AuthToken, error) { 34 + if len(value) == 0 { 35 + return nil, nil 36 + } 37 + 38 + parts := strings.SplitN(value, ":", 2) 39 + if len(parts) != 2 { 40 + return nil, ErrAuthTokenInvalidFormat 41 + } 42 + 43 + t, err := auth_model.GetAuthTokenByID(ctx, parts[0]) 44 + if err != nil { 45 + if errors.Is(err, util.ErrNotExist) { 46 + return nil, ErrAuthTokenExpired 47 + } 48 + return nil, err 49 + } 50 + 51 + if t.ExpiresUnix < timeutil.TimeStampNow() { 52 + return nil, ErrAuthTokenExpired 53 + } 54 + 55 + hashedToken := sha256.Sum256([]byte(parts[1])) 56 + 57 + if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(hex.EncodeToString(hashedToken[:]))) == 0 { 58 + // If an attacker steals a token and uses the token to create a new session the hash gets updated. 59 + // When the victim uses the old token the hashes don't match anymore and the victim should be notified about the compromised token. 60 + return nil, ErrAuthTokenInvalidHash 61 + } 62 + 63 + return t, nil 64 + } 65 + 66 + func RegenerateAuthToken(ctx context.Context, t *auth_model.AuthToken) (*auth_model.AuthToken, string, error) { 67 + token, hash, err := generateTokenAndHash() 68 + if err != nil { 69 + return nil, "", err 70 + } 71 + 72 + newToken := &auth_model.AuthToken{ 73 + ID: t.ID, 74 + TokenHash: hash, 75 + UserID: t.UserID, 76 + ExpiresUnix: timeutil.TimeStampNow().AddDuration(time.Duration(setting.LogInRememberDays*24) * time.Hour), 77 + } 78 + 79 + if err := auth_model.UpdateAuthTokenByID(ctx, newToken); err != nil { 80 + return nil, "", err 81 + } 82 + 83 + return newToken, token, nil 84 + } 85 + 86 + func CreateAuthTokenForUserID(ctx context.Context, userID int64) (*auth_model.AuthToken, string, error) { 87 + t := &auth_model.AuthToken{ 88 + UserID: userID, 89 + ExpiresUnix: timeutil.TimeStampNow().AddDuration(time.Duration(setting.LogInRememberDays*24) * time.Hour), 90 + } 91 + 92 + var err error 93 + t.ID, err = util.CryptoRandomString(10) 94 + if err != nil { 95 + return nil, "", err 96 + } 97 + 98 + token, hash, err := generateTokenAndHash() 99 + if err != nil { 100 + return nil, "", err 101 + } 102 + 103 + t.TokenHash = hash 104 + 105 + if err := auth_model.InsertAuthToken(ctx, t); err != nil { 106 + return nil, "", err 107 + } 108 + 109 + return t, token, nil 110 + } 111 + 112 + func generateTokenAndHash() (string, string, error) { 113 + buf, err := util.CryptoRandomBytes(32) 114 + if err != nil { 115 + return "", "", err 116 + } 117 + 118 + token := hex.EncodeToString(buf) 119 + 120 + hashedToken := sha256.Sum256([]byte(token)) 121 + 122 + return token, hex.EncodeToString(hashedToken[:]), nil 123 + }
+107
services/auth/auth_token_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package auth 5 + 6 + import ( 7 + "testing" 8 + "time" 9 + 10 + auth_model "code.gitea.io/gitea/models/auth" 11 + "code.gitea.io/gitea/models/db" 12 + "code.gitea.io/gitea/models/unittest" 13 + "code.gitea.io/gitea/modules/timeutil" 14 + 15 + "github.com/stretchr/testify/assert" 16 + ) 17 + 18 + func TestCheckAuthToken(t *testing.T) { 19 + assert.NoError(t, unittest.PrepareTestDatabase()) 20 + 21 + t.Run("Empty", func(t *testing.T) { 22 + token, err := CheckAuthToken(db.DefaultContext, "") 23 + assert.NoError(t, err) 24 + assert.Nil(t, token) 25 + }) 26 + 27 + t.Run("InvalidFormat", func(t *testing.T) { 28 + token, err := CheckAuthToken(db.DefaultContext, "dummy") 29 + assert.ErrorIs(t, err, ErrAuthTokenInvalidFormat) 30 + assert.Nil(t, token) 31 + }) 32 + 33 + t.Run("NotFound", func(t *testing.T) { 34 + token, err := CheckAuthToken(db.DefaultContext, "notexists:dummy") 35 + assert.ErrorIs(t, err, ErrAuthTokenExpired) 36 + assert.Nil(t, token) 37 + }) 38 + 39 + t.Run("Expired", func(t *testing.T) { 40 + timeutil.Set(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)) 41 + 42 + at, token, err := CreateAuthTokenForUserID(db.DefaultContext, 2) 43 + assert.NoError(t, err) 44 + assert.NotNil(t, at) 45 + assert.NotEmpty(t, token) 46 + 47 + timeutil.Unset() 48 + 49 + at2, err := CheckAuthToken(db.DefaultContext, at.ID+":"+token) 50 + assert.ErrorIs(t, err, ErrAuthTokenExpired) 51 + assert.Nil(t, at2) 52 + 53 + assert.NoError(t, auth_model.DeleteAuthTokenByID(db.DefaultContext, at.ID)) 54 + }) 55 + 56 + t.Run("InvalidHash", func(t *testing.T) { 57 + at, token, err := CreateAuthTokenForUserID(db.DefaultContext, 2) 58 + assert.NoError(t, err) 59 + assert.NotNil(t, at) 60 + assert.NotEmpty(t, token) 61 + 62 + at2, err := CheckAuthToken(db.DefaultContext, at.ID+":"+token+"dummy") 63 + assert.ErrorIs(t, err, ErrAuthTokenInvalidHash) 64 + assert.Nil(t, at2) 65 + 66 + assert.NoError(t, auth_model.DeleteAuthTokenByID(db.DefaultContext, at.ID)) 67 + }) 68 + 69 + t.Run("Valid", func(t *testing.T) { 70 + at, token, err := CreateAuthTokenForUserID(db.DefaultContext, 2) 71 + assert.NoError(t, err) 72 + assert.NotNil(t, at) 73 + assert.NotEmpty(t, token) 74 + 75 + at2, err := CheckAuthToken(db.DefaultContext, at.ID+":"+token) 76 + assert.NoError(t, err) 77 + assert.NotNil(t, at2) 78 + 79 + assert.NoError(t, auth_model.DeleteAuthTokenByID(db.DefaultContext, at.ID)) 80 + }) 81 + } 82 + 83 + func TestRegenerateAuthToken(t *testing.T) { 84 + assert.NoError(t, unittest.PrepareTestDatabase()) 85 + 86 + timeutil.Set(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)) 87 + defer timeutil.Unset() 88 + 89 + at, token, err := CreateAuthTokenForUserID(db.DefaultContext, 2) 90 + assert.NoError(t, err) 91 + assert.NotNil(t, at) 92 + assert.NotEmpty(t, token) 93 + 94 + timeutil.Set(time.Date(2023, 1, 1, 0, 0, 1, 0, time.UTC)) 95 + 96 + at2, token2, err := RegenerateAuthToken(db.DefaultContext, at) 97 + assert.NoError(t, err) 98 + assert.NotNil(t, at2) 99 + assert.NotEmpty(t, token2) 100 + 101 + assert.Equal(t, at.ID, at2.ID) 102 + assert.Equal(t, at.UserID, at2.UserID) 103 + assert.NotEqual(t, token, token2) 104 + assert.NotEqual(t, at.ExpiresUnix, at2.ExpiresUnix) 105 + 106 + assert.NoError(t, auth_model.DeleteAuthTokenByID(db.DefaultContext, at.ID)) 107 + }
+14
services/auth/main_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package auth 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/models/unittest" 10 + ) 11 + 12 + func TestMain(m *testing.M) { 13 + unittest.MainTest(m) 14 + }
+36
tests/integration/signin_test.go
··· 5 5 6 6 import ( 7 7 "net/http" 8 + "net/url" 8 9 "strings" 9 10 "testing" 10 11 11 12 "code.gitea.io/gitea/models/unittest" 12 13 user_model "code.gitea.io/gitea/models/user" 14 + "code.gitea.io/gitea/modules/setting" 13 15 "code.gitea.io/gitea/modules/translation" 14 16 "code.gitea.io/gitea/tests" 15 17 ··· 57 59 testLoginFailed(t, s.username, s.password, s.message) 58 60 } 59 61 } 62 + 63 + func TestSigninWithRememberMe(t *testing.T) { 64 + defer tests.PrepareTestEnv(t)() 65 + 66 + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 67 + baseURL, _ := url.Parse(setting.AppURL) 68 + 69 + session := emptyTestSession(t) 70 + req := NewRequestWithValues(t, "POST", "/user/login", map[string]string{ 71 + "_csrf": GetCSRF(t, session, "/user/login"), 72 + "user_name": user.Name, 73 + "password": userPassword, 74 + "remember": "on", 75 + }) 76 + session.MakeRequest(t, req, http.StatusSeeOther) 77 + 78 + c := session.GetCookie(setting.CookieRememberName) 79 + assert.NotNil(t, c) 80 + 81 + session = emptyTestSession(t) 82 + 83 + // Without session the settings page should not be reachable 84 + req = NewRequest(t, "GET", "/user/settings") 85 + session.MakeRequest(t, req, http.StatusSeeOther) 86 + 87 + req = NewRequest(t, "GET", "/user/login") 88 + // Set the remember me cookie for the login GET request 89 + session.jar.SetCookies(baseURL, []*http.Cookie{c}) 90 + session.MakeRequest(t, req, http.StatusSeeOther) 91 + 92 + // With session the settings page should be reachable 93 + req = NewRequest(t, "GET", "/user/settings") 94 + session.MakeRequest(t, req, http.StatusOK) 95 + }