github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/authentication.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "net/http" 8 "strings" 9 10 "github.com/mattermost/mattermost-server/v5/model" 11 "github.com/mattermost/mattermost-server/v5/services/mfa" 12 "github.com/mattermost/mattermost-server/v5/utils" 13 ) 14 15 type TokenLocation int 16 17 const ( 18 TokenLocationNotFound TokenLocation = iota 19 TokenLocationHeader 20 TokenLocationCookie 21 TokenLocationQueryString 22 TokenLocationCloudHeader 23 ) 24 25 func (tl TokenLocation) String() string { 26 switch tl { 27 case TokenLocationNotFound: 28 return "Not Found" 29 case TokenLocationHeader: 30 return "Header" 31 case TokenLocationCookie: 32 return "Cookie" 33 case TokenLocationQueryString: 34 return "QueryString" 35 case TokenLocationCloudHeader: 36 return "CloudHeader" 37 default: 38 return "Unknown" 39 } 40 } 41 42 func (a *App) IsPasswordValid(password string) *model.AppError { 43 44 if *a.Config().ServiceSettings.EnableDeveloper { 45 return nil 46 } 47 48 return utils.IsPasswordValidWithSettings(password, &a.Config().PasswordSettings) 49 } 50 51 func (a *App) CheckPasswordAndAllCriteria(user *model.User, password string, mfaToken string) *model.AppError { 52 if err := a.CheckUserPreflightAuthenticationCriteria(user, mfaToken); err != nil { 53 return err 54 } 55 56 if err := a.checkUserPassword(user, password); err != nil { 57 if passErr := a.Srv().Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); passErr != nil { 58 return model.NewAppError("CheckPasswordAndAllCriteria", "app.user.update_failed_pwd_attempts.app_error", nil, passErr.Error(), http.StatusInternalServerError) 59 } 60 61 a.InvalidateCacheForUser(user.Id) 62 63 return err 64 } 65 66 if err := a.CheckUserMfa(user, mfaToken); err != nil { 67 // If the mfaToken is not set, we assume the client used this as a pre-flight request to query the server 68 // about the MFA state of the user in question 69 if mfaToken != "" { 70 if passErr := a.Srv().Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); passErr != nil { 71 return model.NewAppError("CheckPasswordAndAllCriteria", "app.user.update_failed_pwd_attempts.app_error", nil, passErr.Error(), http.StatusInternalServerError) 72 } 73 } 74 75 a.InvalidateCacheForUser(user.Id) 76 77 return err 78 } 79 80 if passErr := a.Srv().Store.User().UpdateFailedPasswordAttempts(user.Id, 0); passErr != nil { 81 return model.NewAppError("CheckPasswordAndAllCriteria", "app.user.update_failed_pwd_attempts.app_error", nil, passErr.Error(), http.StatusInternalServerError) 82 } 83 84 a.InvalidateCacheForUser(user.Id) 85 86 if err := a.CheckUserPostflightAuthenticationCriteria(user); err != nil { 87 return err 88 } 89 90 return nil 91 } 92 93 // This to be used for places we check the users password when they are already logged in 94 func (a *App) DoubleCheckPassword(user *model.User, password string) *model.AppError { 95 if err := checkUserLoginAttempts(user, *a.Config().ServiceSettings.MaximumLoginAttempts); err != nil { 96 return err 97 } 98 99 if err := a.checkUserPassword(user, password); err != nil { 100 if passErr := a.Srv().Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); passErr != nil { 101 return model.NewAppError("DoubleCheckPassword", "app.user.update_failed_pwd_attempts.app_error", nil, passErr.Error(), http.StatusInternalServerError) 102 } 103 104 a.InvalidateCacheForUser(user.Id) 105 106 return err 107 } 108 109 if passErr := a.Srv().Store.User().UpdateFailedPasswordAttempts(user.Id, 0); passErr != nil { 110 return model.NewAppError("DoubleCheckPassword", "app.user.update_failed_pwd_attempts.app_error", nil, passErr.Error(), http.StatusInternalServerError) 111 } 112 113 a.InvalidateCacheForUser(user.Id) 114 115 return nil 116 } 117 118 func (a *App) checkUserPassword(user *model.User, password string) *model.AppError { 119 if !model.ComparePassword(user.Password, password) { 120 return model.NewAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) 121 } 122 123 return nil 124 } 125 126 func (a *App) checkLdapUserPasswordAndAllCriteria(ldapId *string, password string, mfaToken string) (*model.User, *model.AppError) { 127 if a.Ldap() == nil || ldapId == nil { 128 err := model.NewAppError("doLdapAuthentication", "api.user.login_ldap.not_available.app_error", nil, "", http.StatusNotImplemented) 129 return nil, err 130 } 131 132 ldapUser, err := a.Ldap().DoLogin(*ldapId, password) 133 if err != nil { 134 err.StatusCode = http.StatusUnauthorized 135 return nil, err 136 } 137 138 if err := a.CheckUserMfa(ldapUser, mfaToken); err != nil { 139 return nil, err 140 } 141 142 if err := checkUserNotDisabled(ldapUser); err != nil { 143 return nil, err 144 } 145 146 // user successfully authenticated 147 return ldapUser, nil 148 } 149 150 func (a *App) CheckUserAllAuthenticationCriteria(user *model.User, mfaToken string) *model.AppError { 151 if err := a.CheckUserPreflightAuthenticationCriteria(user, mfaToken); err != nil { 152 return err 153 } 154 155 if err := a.CheckUserPostflightAuthenticationCriteria(user); err != nil { 156 return err 157 } 158 159 return nil 160 } 161 162 func (a *App) CheckUserPreflightAuthenticationCriteria(user *model.User, mfaToken string) *model.AppError { 163 if err := checkUserNotDisabled(user); err != nil { 164 return err 165 } 166 167 if err := checkUserNotBot(user); err != nil { 168 return err 169 } 170 171 if err := checkUserLoginAttempts(user, *a.Config().ServiceSettings.MaximumLoginAttempts); err != nil { 172 return err 173 } 174 175 return nil 176 } 177 178 func (a *App) CheckUserPostflightAuthenticationCriteria(user *model.User) *model.AppError { 179 if !user.EmailVerified && *a.Config().EmailSettings.RequireEmailVerification { 180 return model.NewAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) 181 } 182 183 return nil 184 } 185 186 func (a *App) CheckUserMfa(user *model.User, token string) *model.AppError { 187 if !user.MfaActive || !*a.Config().ServiceSettings.EnableMultifactorAuthentication { 188 return nil 189 } 190 191 mfaService := mfa.New(a, a.Srv().Store) 192 ok, err := mfaService.ValidateToken(user.MfaSecret, token) 193 if err != nil { 194 return err 195 } 196 197 if !ok { 198 return model.NewAppError("checkUserMfa", "api.user.check_user_mfa.bad_code.app_error", nil, "", http.StatusUnauthorized) 199 } 200 201 return nil 202 } 203 204 func checkUserLoginAttempts(user *model.User, max int) *model.AppError { 205 if user.FailedAttempts >= max { 206 return model.NewAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) 207 } 208 209 return nil 210 } 211 212 func checkUserNotDisabled(user *model.User) *model.AppError { 213 if user.DeleteAt > 0 { 214 return model.NewAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) 215 } 216 return nil 217 } 218 219 func checkUserNotBot(user *model.User) *model.AppError { 220 if user.IsBot { 221 return model.NewAppError("Login", "api.user.login.bot_login_forbidden.app_error", nil, "user_id="+user.Id, http.StatusUnauthorized) 222 } 223 return nil 224 } 225 226 func (a *App) authenticateUser(user *model.User, password, mfaToken string) (*model.User, *model.AppError) { 227 license := a.Srv().License() 228 ldapAvailable := *a.Config().LdapSettings.Enable && a.Ldap() != nil && license != nil && *license.Features.LDAP 229 230 if user.AuthService == model.USER_AUTH_SERVICE_LDAP { 231 if !ldapAvailable { 232 err := model.NewAppError("login", "api.user.login_ldap.not_available.app_error", nil, "", http.StatusNotImplemented) 233 return user, err 234 } 235 236 ldapUser, err := a.checkLdapUserPasswordAndAllCriteria(user.AuthData, password, mfaToken) 237 if err != nil { 238 err.StatusCode = http.StatusUnauthorized 239 return user, err 240 } 241 242 // slightly redundant to get the user again, but we need to get it from the LDAP server 243 return ldapUser, nil 244 } 245 246 if user.AuthService != "" { 247 authService := user.AuthService 248 if authService == model.USER_AUTH_SERVICE_SAML { 249 authService = strings.ToUpper(authService) 250 } 251 err := model.NewAppError("login", "api.user.login.use_auth_service.app_error", map[string]interface{}{"AuthService": authService}, "", http.StatusBadRequest) 252 return user, err 253 } 254 255 if err := a.CheckPasswordAndAllCriteria(user, password, mfaToken); err != nil { 256 err.StatusCode = http.StatusUnauthorized 257 return user, err 258 } 259 260 return user, nil 261 } 262 263 func ParseAuthTokenFromRequest(r *http.Request) (string, TokenLocation) { 264 authHeader := r.Header.Get(model.HEADER_AUTH) 265 266 // Attempt to parse the token from the cookie 267 if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil { 268 return cookie.Value, TokenLocationCookie 269 } 270 271 // Parse the token from the header 272 if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER { 273 // Default session token 274 return authHeader[7:], TokenLocationHeader 275 } 276 277 if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN { 278 // OAuth token 279 return authHeader[6:], TokenLocationHeader 280 } 281 282 // Attempt to parse token out of the query string 283 if token := r.URL.Query().Get("access_token"); token != "" { 284 return token, TokenLocationQueryString 285 } 286 287 if token := r.Header.Get(model.HEADER_CLOUD_TOKEN); token != "" { 288 return token, TokenLocationCloudHeader 289 } 290 291 return "", TokenLocationNotFound 292 }