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  }