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