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 }