github.com/vnforks/kid@v5.11.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) CheckForClientSideCert(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  		return nil, model.NewAppError("AuthenticateUserForLogin", "api.user.login.blank_pwd.app_error", nil, "", http.StatusBadRequest)
    49  	}
    50  
    51  	// Get the MM user we are trying to login
    52  	if user, err = a.GetUserForLogin(id, loginId); err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	// If client side cert is enable and it's checking as a primary source
    57  	// then trust the proxy and cert that the correct user is supplied and allow
    58  	// them access
    59  	if *a.Config().ExperimentalSettings.ClientSideCertEnable && *a.Config().ExperimentalSettings.ClientSideCertCheck == model.CLIENT_SIDE_CERT_CHECK_PRIMARY_AUTH {
    60  		// Unless the user is a bot.
    61  		if err = checkUserNotBot(user); err != nil {
    62  			return nil, err
    63  		}
    64  
    65  		return user, nil
    66  	}
    67  
    68  	// and then authenticate them
    69  	if user, err = a.authenticateUser(user, password, mfaToken); err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return user, nil
    74  }
    75  
    76  func (a *App) GetUserForLogin(id, loginId string) (*model.User, *model.AppError) {
    77  	enableUsername := *a.Config().EmailSettings.EnableSignInWithUsername
    78  	enableEmail := *a.Config().EmailSettings.EnableSignInWithEmail
    79  
    80  	// If we are given a userID then fail if we can't find a user with that ID
    81  	if len(id) != 0 {
    82  		user, err := a.GetUser(id)
    83  		if err != nil {
    84  			if err.Id != store.MISSING_ACCOUNT_ERROR {
    85  				err.StatusCode = http.StatusInternalServerError
    86  				return nil, err
    87  			}
    88  			err.StatusCode = http.StatusBadRequest
    89  			return nil, err
    90  		}
    91  		return user, nil
    92  	}
    93  
    94  	// Try to get the user by username/email
    95  	if result := <-a.Srv.Store.User().GetForLogin(loginId, enableUsername, enableEmail); result.Err == nil {
    96  		return result.Data.(*model.User), nil
    97  	}
    98  
    99  	// Try to get the user with LDAP if enabled
   100  	if *a.Config().LdapSettings.Enable && a.Ldap != nil {
   101  		if ldapUser, err := a.Ldap.GetUser(loginId); err == nil {
   102  			if user, err := a.GetUserByAuth(ldapUser.AuthData, model.USER_AUTH_SERVICE_LDAP); err == nil {
   103  				return user, nil
   104  			}
   105  			return ldapUser, nil
   106  		}
   107  	}
   108  
   109  	return nil, model.NewAppError("GetUserForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusBadRequest)
   110  }
   111  
   112  func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) (*model.Session, *model.AppError) {
   113  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   114  		var rejectionReason string
   115  		pluginContext := a.PluginContext()
   116  		pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   117  			rejectionReason = hooks.UserWillLogIn(pluginContext, user)
   118  			return rejectionReason == ""
   119  		}, plugin.UserWillLogInId)
   120  
   121  		if rejectionReason != "" {
   122  			return nil, model.NewAppError("DoLogin", "Login rejected by plugin: "+rejectionReason, nil, "", http.StatusBadRequest)
   123  		}
   124  	}
   125  
   126  	session := &model.Session{UserId: user.Id, Roles: user.GetRawRoles(), DeviceId: deviceId, IsOAuth: false}
   127  	session.GenerateCSRF()
   128  
   129  	if len(deviceId) > 0 {
   130  		session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthMobileInDays)
   131  
   132  		// A special case where we logout of all other sessions with the same Id
   133  		if err := a.RevokeSessionsForDeviceId(user.Id, deviceId, ""); err != nil {
   134  			err.StatusCode = http.StatusInternalServerError
   135  			return nil, err
   136  		}
   137  	} else {
   138  		session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthWebInDays)
   139  	}
   140  
   141  	ua := uasurfer.Parse(r.UserAgent())
   142  
   143  	plat := getPlatformName(ua)
   144  	os := getOSName(ua)
   145  	bname := getBrowserName(ua, r.UserAgent())
   146  	bversion := getBrowserVersion(ua, r.UserAgent())
   147  
   148  	session.AddProp(model.SESSION_PROP_PLATFORM, plat)
   149  	session.AddProp(model.SESSION_PROP_OS, os)
   150  	session.AddProp(model.SESSION_PROP_BROWSER, fmt.Sprintf("%v/%v", bname, bversion))
   151  
   152  	var err *model.AppError
   153  	if session, err = a.CreateSession(session); err != nil {
   154  		err.StatusCode = http.StatusInternalServerError
   155  		return nil, err
   156  	}
   157  
   158  	w.Header().Set(model.HEADER_TOKEN, session.Token)
   159  
   160  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   161  		a.Srv.Go(func() {
   162  			pluginContext := a.PluginContext()
   163  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   164  				hooks.UserHasLoggedIn(pluginContext, user)
   165  				return true
   166  			}, plugin.UserHasLoggedInId)
   167  		})
   168  	}
   169  
   170  	return session, nil
   171  }
   172  
   173  func (a *App) AttachSessionCookies(w http.ResponseWriter, r *http.Request, session *model.Session) {
   174  	secure := false
   175  	if GetProtocol(r) == "https" {
   176  		secure = true
   177  	}
   178  
   179  	maxAge := *a.Config().ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24
   180  	domain := a.GetCookieDomain()
   181  	expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0)
   182  	sessionCookie := &http.Cookie{
   183  		Name:     model.SESSION_COOKIE_TOKEN,
   184  		Value:    session.Token,
   185  		Path:     "/",
   186  		MaxAge:   maxAge,
   187  		Expires:  expiresAt,
   188  		HttpOnly: true,
   189  		Domain:   domain,
   190  		Secure:   secure,
   191  	}
   192  
   193  	userCookie := &http.Cookie{
   194  		Name:    model.SESSION_COOKIE_USER,
   195  		Value:   session.UserId,
   196  		Path:    "/",
   197  		MaxAge:  maxAge,
   198  		Expires: expiresAt,
   199  		Domain:  domain,
   200  		Secure:  secure,
   201  	}
   202  
   203  	csrfCookie := &http.Cookie{
   204  		Name:    model.SESSION_COOKIE_CSRF,
   205  		Value:   session.GetCSRF(),
   206  		Path:    "/",
   207  		MaxAge:  maxAge,
   208  		Expires: expiresAt,
   209  		Domain:  domain,
   210  		Secure:  secure,
   211  	}
   212  
   213  	http.SetCookie(w, sessionCookie)
   214  	http.SetCookie(w, userCookie)
   215  	http.SetCookie(w, csrfCookie)
   216  }
   217  
   218  func GetProtocol(r *http.Request) string {
   219  	if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" || r.TLS != nil {
   220  		return "https"
   221  	}
   222  	return "http"
   223  }