code.gitea.io/gitea@v1.21.7/services/auth/basic.go (about) 1 // Copyright 2014 The Gogs Authors. All rights reserved. 2 // Copyright 2019 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package auth 6 7 import ( 8 "net/http" 9 "strings" 10 11 actions_model "code.gitea.io/gitea/models/actions" 12 auth_model "code.gitea.io/gitea/models/auth" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/base" 15 "code.gitea.io/gitea/modules/log" 16 "code.gitea.io/gitea/modules/setting" 17 "code.gitea.io/gitea/modules/timeutil" 18 "code.gitea.io/gitea/modules/util" 19 "code.gitea.io/gitea/modules/web/middleware" 20 ) 21 22 // Ensure the struct implements the interface. 23 var ( 24 _ Method = &Basic{} 25 ) 26 27 // BasicMethodName is the constant name of the basic authentication method 28 const BasicMethodName = "basic" 29 30 // Basic implements the Auth interface and authenticates requests (API requests 31 // only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization" 32 // header. 33 type Basic struct{} 34 35 // Name represents the name of auth method 36 func (b *Basic) Name() string { 37 return BasicMethodName 38 } 39 40 // Verify extracts and validates Basic data (username and password/token) from the 41 // "Authorization" header of the request and returns the corresponding user object for that 42 // name/token on successful validation. 43 // Returns nil if header is empty or validation fails. 44 func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { 45 // Basic authentication should only fire on API, Download or on Git or LFSPaths 46 if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) { 47 return nil, nil 48 } 49 50 baHead := req.Header.Get("Authorization") 51 if len(baHead) == 0 { 52 return nil, nil 53 } 54 55 auths := strings.SplitN(baHead, " ", 2) 56 if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") { 57 return nil, nil 58 } 59 60 uname, passwd, _ := base.BasicAuthDecode(auths[1]) 61 62 // Check if username or password is a token 63 isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic" 64 // Assume username is token 65 authToken := uname 66 if !isUsernameToken { 67 log.Trace("Basic Authorization: Attempting login for: %s", uname) 68 // Assume password is token 69 authToken = passwd 70 } else { 71 log.Trace("Basic Authorization: Attempting login with username as token") 72 } 73 74 // check oauth2 token 75 uid := CheckOAuthAccessToken(req.Context(), authToken) 76 if uid != 0 { 77 log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid) 78 79 u, err := user_model.GetUserByID(req.Context(), uid) 80 if err != nil { 81 log.Error("GetUserByID: %v", err) 82 return nil, err 83 } 84 85 store.GetData()["IsApiToken"] = true 86 return u, nil 87 } 88 89 // check personal access token 90 token, err := auth_model.GetAccessTokenBySHA(req.Context(), authToken) 91 if err == nil { 92 log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid) 93 u, err := user_model.GetUserByID(req.Context(), token.UID) 94 if err != nil { 95 log.Error("GetUserByID: %v", err) 96 return nil, err 97 } 98 99 token.UpdatedUnix = timeutil.TimeStampNow() 100 if err = auth_model.UpdateAccessToken(req.Context(), token); err != nil { 101 log.Error("UpdateAccessToken: %v", err) 102 } 103 104 store.GetData()["IsApiToken"] = true 105 store.GetData()["ApiTokenScope"] = token.Scope 106 return u, nil 107 } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) { 108 log.Error("GetAccessTokenBySha: %v", err) 109 } 110 111 // check task token 112 task, err := actions_model.GetRunningTaskByToken(req.Context(), authToken) 113 if err == nil && task != nil { 114 log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID) 115 116 store.GetData()["IsActionsToken"] = true 117 store.GetData()["ActionsTaskID"] = task.ID 118 119 return user_model.NewActionsUser(), nil 120 } 121 122 if !setting.Service.EnableBasicAuth { 123 return nil, nil 124 } 125 126 log.Trace("Basic Authorization: Attempting SignIn for %s", uname) 127 u, source, err := UserSignIn(req.Context(), uname, passwd) 128 if err != nil { 129 if !user_model.IsErrUserNotExist(err) { 130 log.Error("UserSignIn: %v", err) 131 } 132 return nil, err 133 } 134 135 if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { 136 if err := validateTOTP(req, u); err != nil { 137 return nil, err 138 } 139 } 140 141 log.Trace("Basic Authorization: Logged in user %-v", u) 142 143 return u, nil 144 } 145 146 func validateTOTP(req *http.Request, u *user_model.User) error { 147 twofa, err := auth_model.GetTwoFactorByUID(req.Context(), u.ID) 148 if err != nil { 149 if auth_model.IsErrTwoFactorNotEnrolled(err) { 150 // No 2FA enrollment for this user 151 return nil 152 } 153 return err 154 } 155 if ok, err := twofa.ValidateTOTP(req.Header.Get("X-Gitea-OTP")); err != nil { 156 return err 157 } else if !ok { 158 return util.NewInvalidArgumentErrorf("invalid provided OTP") 159 } 160 return nil 161 }