code.gitea.io/gitea@v1.22.3/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 ( 29 BasicMethodName = "basic" 30 AccessTokenMethodName = "access_token" 31 OAuth2TokenMethodName = "oauth2_token" 32 ActionTokenMethodName = "action_token" 33 ) 34 35 // Basic implements the Auth interface and authenticates requests (API requests 36 // only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization" 37 // header. 38 type Basic struct{} 39 40 // Name represents the name of auth method 41 func (b *Basic) Name() string { 42 return BasicMethodName 43 } 44 45 // Verify extracts and validates Basic data (username and password/token) from the 46 // "Authorization" header of the request and returns the corresponding user object for that 47 // name/token on successful validation. 48 // Returns nil if header is empty or validation fails. 49 func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { 50 // Basic authentication should only fire on API, Download or on Git or LFSPaths 51 if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) { 52 return nil, nil 53 } 54 55 baHead := req.Header.Get("Authorization") 56 if len(baHead) == 0 { 57 return nil, nil 58 } 59 60 auths := strings.SplitN(baHead, " ", 2) 61 if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") { 62 return nil, nil 63 } 64 65 uname, passwd, _ := base.BasicAuthDecode(auths[1]) 66 67 // Check if username or password is a token 68 isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic" 69 // Assume username is token 70 authToken := uname 71 if !isUsernameToken { 72 log.Trace("Basic Authorization: Attempting login for: %s", uname) 73 // Assume password is token 74 authToken = passwd 75 } else { 76 log.Trace("Basic Authorization: Attempting login with username as token") 77 } 78 79 // check oauth2 token 80 uid := CheckOAuthAccessToken(req.Context(), authToken) 81 if uid != 0 { 82 log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid) 83 84 u, err := user_model.GetUserByID(req.Context(), uid) 85 if err != nil { 86 log.Error("GetUserByID: %v", err) 87 return nil, err 88 } 89 90 store.GetData()["LoginMethod"] = OAuth2TokenMethodName 91 store.GetData()["IsApiToken"] = true 92 return u, nil 93 } 94 95 // check personal access token 96 token, err := auth_model.GetAccessTokenBySHA(req.Context(), authToken) 97 if err == nil { 98 log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid) 99 u, err := user_model.GetUserByID(req.Context(), token.UID) 100 if err != nil { 101 log.Error("GetUserByID: %v", err) 102 return nil, err 103 } 104 105 token.UpdatedUnix = timeutil.TimeStampNow() 106 if err = auth_model.UpdateAccessToken(req.Context(), token); err != nil { 107 log.Error("UpdateAccessToken: %v", err) 108 } 109 110 store.GetData()["LoginMethod"] = AccessTokenMethodName 111 store.GetData()["IsApiToken"] = true 112 store.GetData()["ApiTokenScope"] = token.Scope 113 return u, nil 114 } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) { 115 log.Error("GetAccessTokenBySha: %v", err) 116 } 117 118 // check task token 119 task, err := actions_model.GetRunningTaskByToken(req.Context(), authToken) 120 if err == nil && task != nil { 121 log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID) 122 123 store.GetData()["LoginMethod"] = ActionTokenMethodName 124 store.GetData()["IsActionsToken"] = true 125 store.GetData()["ActionsTaskID"] = task.ID 126 127 return user_model.NewActionsUser(), nil 128 } 129 130 if !setting.Service.EnableBasicAuth { 131 return nil, nil 132 } 133 134 log.Trace("Basic Authorization: Attempting SignIn for %s", uname) 135 u, source, err := UserSignIn(req.Context(), uname, passwd) 136 if err != nil { 137 if !user_model.IsErrUserNotExist(err) { 138 log.Error("UserSignIn: %v", err) 139 } 140 return nil, err 141 } 142 143 if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() { 144 if err := validateTOTP(req, u); err != nil { 145 return nil, err 146 } 147 } 148 149 store.GetData()["LoginMethod"] = BasicMethodName 150 log.Trace("Basic Authorization: Logged in user %-v", u) 151 152 return u, nil 153 } 154 155 func validateTOTP(req *http.Request, u *user_model.User) error { 156 twofa, err := auth_model.GetTwoFactorByUID(req.Context(), u.ID) 157 if err != nil { 158 if auth_model.IsErrTwoFactorNotEnrolled(err) { 159 // No 2FA enrollment for this user 160 return nil 161 } 162 return err 163 } 164 if ok, err := twofa.ValidateTOTP(req.Header.Get("X-Gitea-OTP")); err != nil { 165 return err 166 } else if !ok { 167 return util.NewInvalidArgumentErrorf("invalid provided OTP") 168 } 169 return nil 170 } 171 172 func GetAccessScope(store DataStore) auth_model.AccessTokenScope { 173 if v, ok := store.GetData()["ApiTokenScope"]; ok { 174 return v.(auth_model.AccessTokenScope) 175 } 176 switch store.GetData()["LoginMethod"] { 177 case OAuth2TokenMethodName: 178 fallthrough 179 case BasicMethodName, AccessTokenMethodName: 180 return auth_model.AccessTokenScopeAll 181 case ActionTokenMethodName: 182 fallthrough 183 default: 184 return "" 185 } 186 }