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