github.com/vnforks/kid@v5.11.1+incompatible/app/authentication.go (about)

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