github.com/turgay/mattermost-server@v5.3.2-0.20181002173352-2945e8a2b0ce+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  		user, err := a.GetUser(id)
   100  		if err != nil {
   101  			if err.Id != store.MISSING_ACCOUNT_ERROR {
   102  				err.StatusCode = http.StatusInternalServerError
   103  				return nil, err
   104  			}
   105  			err.StatusCode = http.StatusBadRequest
   106  			return nil, err
   107  		}
   108  		return user, nil
   109  	}
   110  
   111  	// Try to get the user by username/email
   112  	if result := <-a.Srv.Store.User().GetForLogin(loginId, enableUsername, enableEmail); result.Err == nil {
   113  		return result.Data.(*model.User), nil
   114  	}
   115  
   116  	// Try to get the user with LDAP if enabled
   117  	if *a.Config().LdapSettings.Enable && a.Ldap != nil {
   118  		if ldapUser, err := a.Ldap.GetUser(loginId); err == nil {
   119  			if user, err := a.GetUserByAuth(ldapUser.AuthData, model.USER_AUTH_SERVICE_LDAP); err == nil {
   120  				return user, nil
   121  			}
   122  			return ldapUser, nil
   123  		}
   124  	}
   125  
   126  	return nil, model.NewAppError("GetUserForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusBadRequest)
   127  }
   128  
   129  func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) (*model.Session, *model.AppError) {
   130  	session := &model.Session{UserId: user.Id, Roles: user.GetRawRoles(), DeviceId: deviceId, IsOAuth: false}
   131  	session.GenerateCSRF()
   132  	maxAge := *a.Config().ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24
   133  
   134  	if len(deviceId) > 0 {
   135  		session.SetExpireInDays(*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 nil, err
   141  		}
   142  	} else {
   143  		session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthWebInDays)
   144  	}
   145  
   146  	ua := uasurfer.Parse(r.UserAgent())
   147  
   148  	plat := getPlatformName(ua)
   149  	os := getOSName(ua)
   150  	bname := getBrowserName(ua, r.UserAgent())
   151  	bversion := getBrowserVersion(ua, r.UserAgent())
   152  
   153  	session.AddProp(model.SESSION_PROP_PLATFORM, plat)
   154  	session.AddProp(model.SESSION_PROP_OS, os)
   155  	session.AddProp(model.SESSION_PROP_BROWSER, fmt.Sprintf("%v/%v", bname, bversion))
   156  
   157  	var err *model.AppError
   158  	if session, err = a.CreateSession(session); err != nil {
   159  		err.StatusCode = http.StatusInternalServerError
   160  		return nil, err
   161  	}
   162  
   163  	w.Header().Set(model.HEADER_TOKEN, session.Token)
   164  
   165  	secure := false
   166  	if GetProtocol(r) == "https" {
   167  		secure = true
   168  	}
   169  
   170  	domain := a.GetCookieDomain()
   171  	expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0)
   172  	sessionCookie := &http.Cookie{
   173  		Name:     model.SESSION_COOKIE_TOKEN,
   174  		Value:    session.Token,
   175  		Path:     "/",
   176  		MaxAge:   maxAge,
   177  		Expires:  expiresAt,
   178  		HttpOnly: true,
   179  		Domain:   domain,
   180  		Secure:   secure,
   181  	}
   182  
   183  	userCookie := &http.Cookie{
   184  		Name:    model.SESSION_COOKIE_USER,
   185  		Value:   user.Id,
   186  		Path:    "/",
   187  		MaxAge:  maxAge,
   188  		Expires: expiresAt,
   189  		Domain:  domain,
   190  		Secure:  secure,
   191  	}
   192  
   193  	http.SetCookie(w, sessionCookie)
   194  	http.SetCookie(w, userCookie)
   195  
   196  	return session, nil
   197  }
   198  
   199  func GetProtocol(r *http.Request) string {
   200  	if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" || r.TLS != nil {
   201  		return "https"
   202  	}
   203  	return "http"
   204  }