github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/login.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  	"fmt"
     8  	"net/http"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/avct/uasurfer"
    14  	"github.com/mattermost/mattermost-server/v5/model"
    15  	"github.com/mattermost/mattermost-server/v5/plugin"
    16  	"github.com/mattermost/mattermost-server/v5/store"
    17  	"github.com/mattermost/mattermost-server/v5/utils"
    18  )
    19  
    20  func (a *App) CheckForClientSideCert(r *http.Request) (string, string, string) {
    21  	pem := r.Header.Get("X-SSL-Client-Cert")                // mapped to $ssl_client_cert from nginx
    22  	subject := r.Header.Get("X-SSL-Client-Cert-Subject-DN") // mapped to $ssl_client_s_dn from nginx
    23  	email := ""
    24  
    25  	if len(subject) > 0 {
    26  		for _, v := range strings.Split(subject, "/") {
    27  			kv := strings.Split(v, "=")
    28  			if len(kv) == 2 && kv[0] == "emailAddress" {
    29  				email = kv[1]
    30  			}
    31  		}
    32  	}
    33  
    34  	return pem, subject, email
    35  }
    36  
    37  func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken string, ldapOnly bool) (user *model.User, err *model.AppError) {
    38  	// Do statistics
    39  	defer func() {
    40  		if a.Metrics() != nil {
    41  			if user == nil || err != nil {
    42  				a.Metrics().IncrementLoginFail()
    43  			} else {
    44  				a.Metrics().IncrementLogin()
    45  			}
    46  		}
    47  	}()
    48  
    49  	if len(password) == 0 {
    50  		return nil, model.NewAppError("AuthenticateUserForLogin", "api.user.login.blank_pwd.app_error", nil, "", http.StatusBadRequest)
    51  	}
    52  
    53  	// Get the MM user we are trying to login
    54  	if user, err = a.GetUserForLogin(id, loginId); err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	// If client side cert is enable and it's checking as a primary source
    59  	// then trust the proxy and cert that the correct user is supplied and allow
    60  	// them access
    61  	if *a.Config().ExperimentalSettings.ClientSideCertEnable && *a.Config().ExperimentalSettings.ClientSideCertCheck == model.CLIENT_SIDE_CERT_CHECK_PRIMARY_AUTH {
    62  		// Unless the user is a bot.
    63  		if err = checkUserNotBot(user); err != nil {
    64  			return nil, err
    65  		}
    66  
    67  		return user, nil
    68  	}
    69  
    70  	// and then authenticate them
    71  	if user, err = a.authenticateUser(user, password, mfaToken); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return user, nil
    76  }
    77  
    78  func (a *App) GetUserForLogin(id, loginId string) (*model.User, *model.AppError) {
    79  	enableUsername := *a.Config().EmailSettings.EnableSignInWithUsername
    80  	enableEmail := *a.Config().EmailSettings.EnableSignInWithEmail
    81  
    82  	// If we are given a userID then fail if we can't find a user with that ID
    83  	if len(id) != 0 {
    84  		user, err := a.GetUser(id)
    85  		if err != nil {
    86  			if err.Id != store.MISSING_ACCOUNT_ERROR {
    87  				err.StatusCode = http.StatusInternalServerError
    88  				return nil, err
    89  			}
    90  			err.StatusCode = http.StatusBadRequest
    91  			return nil, err
    92  		}
    93  		return user, nil
    94  	}
    95  
    96  	// Try to get the user by username/email
    97  	if user, err := a.Srv().Store.User().GetForLogin(loginId, enableUsername, enableEmail); err == nil {
    98  		return user, nil
    99  	}
   100  
   101  	// Try to get the user with LDAP if enabled
   102  	if *a.Config().LdapSettings.Enable && a.Ldap() != nil {
   103  		if ldapUser, err := a.Ldap().GetUser(loginId); err == nil {
   104  			if user, err := a.GetUserByAuth(ldapUser.AuthData, model.USER_AUTH_SERVICE_LDAP); err == nil {
   105  				return user, nil
   106  			}
   107  			return ldapUser, nil
   108  		}
   109  	}
   110  
   111  	return nil, model.NewAppError("GetUserForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusBadRequest)
   112  }
   113  
   114  func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User, deviceId string, isMobile, isOAuth, isSaml bool) *model.AppError {
   115  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   116  		var rejectionReason string
   117  		pluginContext := a.PluginContext()
   118  		pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   119  			rejectionReason = hooks.UserWillLogIn(pluginContext, user)
   120  			return rejectionReason == ""
   121  		}, plugin.UserWillLogInId)
   122  
   123  		if rejectionReason != "" {
   124  			return model.NewAppError("DoLogin", "Login rejected by plugin: "+rejectionReason, nil, "", http.StatusBadRequest)
   125  		}
   126  	}
   127  
   128  	session := &model.Session{UserId: user.Id, Roles: user.GetRawRoles(), DeviceId: deviceId, IsOAuth: isOAuth, Props: map[string]string{
   129  		model.USER_AUTH_SERVICE_IS_MOBILE: strconv.FormatBool(isMobile),
   130  		model.USER_AUTH_SERVICE_IS_SAML:   strconv.FormatBool(isSaml),
   131  	}}
   132  	session.GenerateCSRF()
   133  
   134  	if len(deviceId) > 0 {
   135  		a.SetSessionExpireInDays(session, *a.Config().ServiceSettings.SessionLengthMobileInDays)
   136  
   137  		// A special case where we logout of all other sessions with the same Id
   138  		if err := a.RevokeSessionsForDeviceId(user.Id, deviceId, ""); err != nil {
   139  			err.StatusCode = http.StatusInternalServerError
   140  			return err
   141  		}
   142  	} else if isMobile {
   143  		a.SetSessionExpireInDays(session, *a.Config().ServiceSettings.SessionLengthMobileInDays)
   144  	} else if isOAuth || isSaml {
   145  		a.SetSessionExpireInDays(session, *a.Config().ServiceSettings.SessionLengthSSOInDays)
   146  	} else {
   147  		a.SetSessionExpireInDays(session, *a.Config().ServiceSettings.SessionLengthWebInDays)
   148  	}
   149  
   150  	ua := uasurfer.Parse(r.UserAgent())
   151  
   152  	plat := getPlatformName(ua)
   153  	os := getOSName(ua)
   154  	bname := getBrowserName(ua, r.UserAgent())
   155  	bversion := getBrowserVersion(ua, r.UserAgent())
   156  
   157  	session.AddProp(model.SESSION_PROP_PLATFORM, plat)
   158  	session.AddProp(model.SESSION_PROP_OS, os)
   159  	session.AddProp(model.SESSION_PROP_BROWSER, fmt.Sprintf("%v/%v", bname, bversion))
   160  	if user.IsGuest() {
   161  		session.AddProp(model.SESSION_PROP_IS_GUEST, "true")
   162  	} else {
   163  		session.AddProp(model.SESSION_PROP_IS_GUEST, "false")
   164  	}
   165  
   166  	var err *model.AppError
   167  	if session, err = a.CreateSession(session); err != nil {
   168  		err.StatusCode = http.StatusInternalServerError
   169  		return err
   170  	}
   171  
   172  	w.Header().Set(model.HEADER_TOKEN, session.Token)
   173  
   174  	a.SetSession(session)
   175  
   176  	if user.AuthService == model.USER_AUTH_SERVICE_LDAP && a.Ldap() != nil {
   177  		a.Srv().Go(func() {
   178  			a.Ldap().UpdateProfilePictureIfNecessary(user, session)
   179  		})
   180  	}
   181  
   182  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   183  		a.Srv().Go(func() {
   184  			pluginContext := a.PluginContext()
   185  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   186  				hooks.UserHasLoggedIn(pluginContext, user)
   187  				return true
   188  			}, plugin.UserHasLoggedInId)
   189  		})
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func (a *App) AttachSessionCookies(w http.ResponseWriter, r *http.Request) {
   196  	secure := false
   197  	if GetProtocol(r) == "https" {
   198  		secure = true
   199  	}
   200  
   201  	maxAge := *a.Config().ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24
   202  	domain := a.GetCookieDomain()
   203  	subpath, _ := utils.GetSubpathFromConfig(a.Config())
   204  
   205  	expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0)
   206  	sessionCookie := &http.Cookie{
   207  		Name:     model.SESSION_COOKIE_TOKEN,
   208  		Value:    a.Session().Token,
   209  		Path:     subpath,
   210  		MaxAge:   maxAge,
   211  		Expires:  expiresAt,
   212  		HttpOnly: true,
   213  		Domain:   domain,
   214  		Secure:   secure,
   215  	}
   216  
   217  	userCookie := &http.Cookie{
   218  		Name:    model.SESSION_COOKIE_USER,
   219  		Value:   a.Session().UserId,
   220  		Path:    subpath,
   221  		MaxAge:  maxAge,
   222  		Expires: expiresAt,
   223  		Domain:  domain,
   224  		Secure:  secure,
   225  	}
   226  
   227  	csrfCookie := &http.Cookie{
   228  		Name:    model.SESSION_COOKIE_CSRF,
   229  		Value:   a.Session().GetCSRF(),
   230  		Path:    subpath,
   231  		MaxAge:  maxAge,
   232  		Expires: expiresAt,
   233  		Domain:  domain,
   234  		Secure:  secure,
   235  	}
   236  
   237  	http.SetCookie(w, sessionCookie)
   238  	http.SetCookie(w, userCookie)
   239  	http.SetCookie(w, csrfCookie)
   240  }
   241  
   242  func GetProtocol(r *http.Request) string {
   243  	if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" || r.TLS != nil {
   244  		return "https"
   245  	}
   246  	return "http"
   247  }