MM-4998 Adding LoginIdAttribute to allow LDAP users to change their login ID without losing their account (#8756)

* Adding LoginIdAttribute

* Modifying LDAP to use loginIDAttribute.

* Adding IDAttribute migration and AD objectGUID support.

* Removing unused idea.

* Fix typo.
This commit is contained in:
Christopher Speller
2018-05-10 09:46:09 -07:00
committed by GitHub
parent db6b8f6238
commit d8dd271e43
45 changed files with 150 additions and 153 deletions

View File

@@ -275,6 +275,10 @@ store-mocks: ## Creates mock files.
go get github.com/vektra/mockery/... go get github.com/vektra/mockery/...
$(GOPATH)/bin/mockery -dir store -all -output store/storetest/mocks -note 'Regenerate this file using `make store-mocks`.' $(GOPATH)/bin/mockery -dir store -all -output store/storetest/mocks -note 'Regenerate this file using `make store-mocks`.'
ldap-mocks: ## Creates mock files for ldap.
go get github.com/vektra/mockery/...
GOPATH=$(shell go env GOPATH) $(shell go env GOPATH)/bin/mockery -dir enterprise/ldap -all -output enterprise/ldap/mocks -note 'Regenerate this file using `make ldap-mocks`.'
update-jira-plugin: ## Updates Jira plugin. update-jira-plugin: ## Updates Jira plugin.
go get github.com/mattermost/go-bindata/... go get github.com/mattermost/go-bindata/...
curl -s https://api.github.com/repos/mattermost/mattermost-plugin-jira/releases/latest | grep browser_download_url | grep darwin-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz curl -s https://api.github.com/repos/mattermost/mattermost-plugin-jira/releases/latest | grep browser_download_url | grep darwin-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz

View File

@@ -107,7 +107,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
ldapOnly := props["ldap_only"] == "true" ldapOnly := props["ldap_only"] == "true"
c.LogAudit("attempt - user_id=" + id + " login_id=" + loginId) c.LogAudit("attempt - user_id=" + id + " login_id=" + loginId)
user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId, ldapOnly) user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, ldapOnly)
if err != nil { if err != nil {
c.LogAudit("failure - user_id=" + id + " login_id=" + loginId) c.LogAudit("failure - user_id=" + id + " login_id=" + loginId)
c.Err = err c.Err = err
@@ -1072,7 +1072,7 @@ func checkMfa(c *Context, w http.ResponseWriter, r *http.Request) {
} }
rdata := map[string]string{} rdata := map[string]string{}
if user, err := c.App.GetUserForLogin(loginId, false); err != nil { if user, err := c.App.GetUserForLogin("", loginId); err != nil {
rdata["mfa_required"] = "false" rdata["mfa_required"] = "false"
} else { } else {
rdata["mfa_required"] = strconv.FormatBool(user.MfaActive) rdata["mfa_required"] = strconv.FormatBool(user.MfaActive)

View File

@@ -771,7 +771,7 @@ func checkUserMfa(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
if user, err := c.App.GetUserForLogin(loginId, false); err == nil { if user, err := c.App.GetUserForLogin("", loginId); err == nil {
resp["mfa_required"] = user.MfaActive resp["mfa_required"] = user.MfaActive
} }
@@ -943,7 +943,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
ldapOnly := props["ldap_only"] == "true" ldapOnly := props["ldap_only"] == "true"
c.LogAuditWithUserId(id, "attempt - login_id="+loginId) c.LogAuditWithUserId(id, "attempt - login_id="+loginId)
user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId, ldapOnly) user, err := c.App.AuthenticateUserForLogin(id, loginId, password, mfaToken, ldapOnly)
if err != nil { if err != nil {
c.LogAuditWithUserId(id, "failure - login_id="+loginId) c.LogAuditWithUserId(id, "failure - login_id="+loginId)
c.Err = err c.Err = err
@@ -1167,7 +1167,7 @@ func sendVerificationEmail(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
user, err := c.App.GetUserForLogin(email, false) user, err := c.App.GetUserForLogin("", email)
if err != nil { if err != nil {
// Don't want to leak whether the email is valid or not // Don't want to leak whether the email is valid or not
ReturnStatusOK(w) ReturnStatusOK(w)
@@ -1205,7 +1205,7 @@ func switchAccountType(c *Context, w http.ResponseWriter, r *http.Request) {
link, err = c.App.SwitchOAuthToEmail(switchRequest.Email, switchRequest.NewPassword, c.Session.UserId) link, err = c.App.SwitchOAuthToEmail(switchRequest.Email, switchRequest.NewPassword, c.Session.UserId)
} else if switchRequest.EmailToLdap() { } else if switchRequest.EmailToLdap() {
link, err = c.App.SwitchEmailToLdap(switchRequest.Email, switchRequest.Password, switchRequest.MfaCode, switchRequest.LdapId, switchRequest.NewPassword) link, err = c.App.SwitchEmailToLdap(switchRequest.Email, switchRequest.Password, switchRequest.MfaCode, switchRequest.LdapLoginId, switchRequest.NewPassword)
} else if switchRequest.LdapToEmail() { } else if switchRequest.LdapToEmail() {
link, err = c.App.SwitchLdapToEmail(switchRequest.Password, switchRequest.MfaCode, switchRequest.Email, switchRequest.NewPassword) link, err = c.App.SwitchLdapToEmail(switchRequest.Password, switchRequest.MfaCode, switchRequest.Email, switchRequest.NewPassword)
} else { } else {

View File

@@ -40,7 +40,7 @@ func (a *App) TestLdap() *model.AppError {
return nil return nil
} }
func (a *App) SwitchEmailToLdap(email, password, code, ldapId, ldapPassword string) (string, *model.AppError) { func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword string) (string, *model.AppError) {
if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer { if a.License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer {
return "", model.NewAppError("emailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusForbidden) return "", model.NewAppError("emailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusForbidden)
} }
@@ -63,7 +63,7 @@ func (a *App) SwitchEmailToLdap(email, password, code, ldapId, ldapPassword stri
return "", model.NewAppError("SwitchEmailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusNotImplemented) return "", model.NewAppError("SwitchEmailToLdap", "api.user.email_to_ldap.not_available.app_error", nil, "", http.StatusNotImplemented)
} }
if err := ldapInterface.SwitchToLdap(user.Id, ldapId, ldapPassword); err != nil { if err := ldapInterface.SwitchToLdap(user.Id, ldapLoginId, ldapPassword); err != nil {
return "", err return "", err
} }
@@ -95,7 +95,7 @@ func (a *App) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (
return "", model.NewAppError("SwitchLdapToEmail", "api.user.ldap_to_email.not_available.app_error", nil, "", http.StatusNotImplemented) return "", model.NewAppError("SwitchLdapToEmail", "api.user.ldap_to_email.not_available.app_error", nil, "", http.StatusNotImplemented)
} }
if err := ldapInterface.CheckPassword(*user.AuthData, ldapPassword); err != nil { if err := ldapInterface.CheckPasswordAuthData(*user.AuthData, ldapPassword); err != nil {
return "", err return "", err
} }

View File

@@ -11,47 +11,69 @@ import (
"github.com/avct/uasurfer" "github.com/avct/uasurfer"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
) )
func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId string, ldapOnly bool) (*model.User, *model.AppError) { func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken string, ldapOnly bool) (user *model.User, err *model.AppError) {
// Do statistics
defer func() {
if a.Metrics != nil {
if user == nil || err != nil {
a.Metrics.IncrementLoginFail()
} else {
a.Metrics.IncrementLogin()
}
}
}()
if len(password) == 0 { if len(password) == 0 {
err := model.NewAppError("AuthenticateUserForLogin", "api.user.login.blank_pwd.app_error", nil, "", http.StatusBadRequest) err := model.NewAppError("AuthenticateUserForLogin", "api.user.login.blank_pwd.app_error", nil, "", http.StatusBadRequest)
return nil, err return nil, err
} }
var user *model.User // Get the MM user we are trying to login
var err *model.AppError if user, err = a.GetUserForLogin(id, loginId); err != nil {
if len(id) != 0 {
if user, err = a.GetUser(id); err != nil {
err.StatusCode = http.StatusBadRequest
if a.Metrics != nil {
a.Metrics.IncrementLoginFail()
}
return nil, err return nil, err
} }
} else {
if user, err = a.GetUserForLogin(loginId, ldapOnly); err != nil {
if a.Metrics != nil {
a.Metrics.IncrementLoginFail()
}
return nil, err
}
}
// and then authenticate them // and then authenticate them
if user, err = a.authenticateUser(user, password, mfaToken); err != nil { if user, err = a.authenticateUser(user, password, mfaToken); err != nil {
if a.Metrics != nil {
a.Metrics.IncrementLoginFail()
}
return nil, err return nil, err
} }
if a.Metrics != nil { return user, nil
a.Metrics.IncrementLogin() }
func (a *App) GetUserForLogin(id, loginId string) (*model.User, *model.AppError) {
enableUsername := *a.Config().EmailSettings.EnableSignInWithUsername
enableEmail := *a.Config().EmailSettings.EnableSignInWithEmail
// If we are given a userID then fail if we can't find a user with that ID
if len(id) != 0 {
if user, err := a.GetUser(id); err != nil {
if err.Id != store.MISSING_ACCOUNT_ERROR {
err.StatusCode = http.StatusInternalServerError
return nil, err
} else {
err.StatusCode = http.StatusBadRequest
return nil, err
}
} else {
return user, nil
}
} }
// Try to get the user by username/email
if result := <-a.Srv.Store.User().GetForLogin(loginId, enableUsername, enableEmail); result.Err == nil {
return result.Data.(*model.User), nil
}
// Try to get the user with LDAP
if user, err := a.Ldap.GetUser(loginId); err == nil {
return user, nil return user, nil
}
return nil, model.NewAppError("GetUserForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusBadRequest)
} }
func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) (*model.Session, *model.AppError) { func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) (*model.Session, *model.AppError) {

View File

@@ -382,38 +382,6 @@ func (a *App) GetUserByAuth(authData *string, authService string) (*model.User,
} }
} }
func (a *App) GetUserForLogin(loginId string, onlyLdap bool) (*model.User, *model.AppError) {
license := a.License()
ldapAvailable := *a.Config().LdapSettings.Enable && a.Ldap != nil && license != nil && *license.Features.LDAP
if result := <-a.Srv.Store.User().GetForLogin(
loginId,
*a.Config().EmailSettings.EnableSignInWithUsername && !onlyLdap,
*a.Config().EmailSettings.EnableSignInWithEmail && !onlyLdap,
ldapAvailable,
); result.Err != nil && result.Err.Id == "store.sql_user.get_for_login.multiple_users" {
// don't fall back to LDAP in this case since we already know there's an LDAP user, but that it shouldn't work
result.Err.StatusCode = http.StatusBadRequest
return nil, result.Err
} else if result.Err != nil {
if !ldapAvailable {
// failed to find user and no LDAP server to fall back on
result.Err.StatusCode = http.StatusBadRequest
return nil, result.Err
}
// fall back to LDAP server to see if we can find a user
if ldapUser, ldapErr := a.Ldap.GetUser(loginId); ldapErr != nil {
ldapErr.StatusCode = http.StatusBadRequest
return nil, ldapErr
} else {
return ldapUser, nil
}
} else {
return result.Data.(*model.User), nil
}
}
func (a *App) GetUsers(offset int, limit int) ([]*model.User, *model.AppError) { func (a *App) GetUsers(offset int, limit int) ([]*model.User, *model.AppError) {
if result := <-a.Srv.Store.User().GetAllProfiles(offset, limit); result.Err != nil { if result := <-a.Srv.Store.User().GetAllProfiles(offset, limit); result.Err != nil {
return nil, result.Err return nil, result.Err

View File

@@ -22,9 +22,19 @@ var LdapSyncCmd = &cobra.Command{
RunE: ldapSyncCmdF, RunE: ldapSyncCmdF,
} }
var LdapIdMigrate = &cobra.Command{
Use: "idmigrate",
Short: "Migrate LDAP IdAttribute to new value",
Long: "Migrate LDAP IdAttribute to new value. Run this utility then change the IdAttribute to the new value.",
Example: " ldap idmigrate objectGUID",
Args: cobra.ExactArgs(1),
RunE: ldapIdMigrateCmdF,
}
func init() { func init() {
LdapCmd.AddCommand( LdapCmd.AddCommand(
LdapSyncCmd, LdapSyncCmd,
LdapIdMigrate,
) )
cmd.RootCmd.AddCommand(LdapCmd) cmd.RootCmd.AddCommand(LdapCmd)
} }
@@ -47,3 +57,22 @@ func ldapSyncCmdF(command *cobra.Command, args []string) error {
return nil return nil
} }
func ldapIdMigrateCmdF(command *cobra.Command, args []string) error {
a, err := cmd.InitDBCommandContextCobra(command)
if err != nil {
return err
}
defer a.Shutdown()
toAttribute := args[0]
if ldapI := a.Ldap; ldapI != nil {
if err := ldapI.MigrateIDAttribute(toAttribute); err != nil {
cmd.CommandPrintErrorln("ERROR: AD/LDAP IdAttribute migration failed! Error: " + err.Error())
} else {
cmd.CommandPrettyPrintln("SUCCESS: AD/LDAP IdAttribute migration complete. You can now change your IdAttribute to: " + toAttribute)
}
}
return nil
}

View File

@@ -263,6 +263,7 @@
"NicknameAttribute": "", "NicknameAttribute": "",
"IdAttribute": "", "IdAttribute": "",
"PositionAttribute": "", "PositionAttribute": "",
"LoginIdAttribute": "",
"SyncIntervalMinutes": 60, "SyncIntervalMinutes": 60,
"SkipCertificateVerification": false, "SkipCertificateVerification": false,
"QueryTimeout": 60, "QueryTimeout": 60,

View File

@@ -4,8 +4,6 @@
package einterfaces package einterfaces
import ( import (
"github.com/go-ldap/ldap"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
) )
@@ -14,12 +12,11 @@ type LdapInterface interface {
GetUser(id string) (*model.User, *model.AppError) GetUser(id string) (*model.User, *model.AppError)
GetUserAttributes(id string, attributes []string) (map[string]string, *model.AppError) GetUserAttributes(id string, attributes []string) (map[string]string, *model.AppError)
CheckPassword(id string, password string) *model.AppError CheckPassword(id string, password string) *model.AppError
CheckPasswordAuthData(authData string, password string) *model.AppError
SwitchToLdap(userId, ldapId, ldapPassword string) *model.AppError SwitchToLdap(userId, ldapId, ldapPassword string) *model.AppError
ValidateFilter(filter string) *model.AppError ValidateFilter(filter string) *model.AppError
StartSynchronizeJob(waitForJobToFinish bool) (*model.Job, *model.AppError) StartSynchronizeJob(waitForJobToFinish bool) (*model.Job, *model.AppError)
RunTest() *model.AppError RunTest() *model.AppError
GetAllLdapUsers() ([]*model.User, *model.AppError) GetAllLdapUsers() ([]*model.User, *model.AppError)
UserFromLdapUser(ldapUser *ldap.Entry) *model.User MigrateIDAttribute(toAttribute string) error
UserHasUpdateFromLdap(existingUser *model.User, currentLdapUser *model.User) bool
UpdateLocalLdapUser(existingUser *model.User, currentLdapUser *model.User) *model.User
} }

View File

@@ -5018,6 +5018,10 @@
"id": "model.config.is_valid.ldap_id", "id": "model.config.is_valid.ldap_id",
"translation": "AD/LDAP field \"ID Attribute\" is required." "translation": "AD/LDAP field \"ID Attribute\" is required."
}, },
{
"id": "model.config.is_valid.ldap_login_id",
"translation": "AD/LDAP field \"Login ID Attribute\" is required."
},
{ {
"id": "model.config.is_valid.ldap_lastname", "id": "model.config.is_valid.ldap_lastname",
"translation": "AD/LDAP field \"Last Name Attribute\" is required." "translation": "AD/LDAP field \"Last Name Attribute\" is required."

View File

@@ -29,6 +29,7 @@ type Field = zapcore.Field
var Int64 = zap.Int64 var Int64 = zap.Int64
var Int = zap.Int var Int = zap.Int
var String = zap.String var String = zap.String
var Any = zap.Any
var Err = zap.Error var Err = zap.Error
type LoggerConfiguration struct { type LoggerConfiguration struct {

View File

@@ -1144,6 +1144,7 @@ type LdapSettings struct {
NicknameAttribute *string NicknameAttribute *string
IdAttribute *string IdAttribute *string
PositionAttribute *string PositionAttribute *string
LoginIdAttribute *string
// Synchronization // Synchronization
SyncIntervalMinutes *int SyncIntervalMinutes *int
@@ -1227,6 +1228,12 @@ func (s *LdapSettings) SetDefaults() {
s.PositionAttribute = NewString(LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE) s.PositionAttribute = NewString(LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE)
} }
// For those upgrading to the version when LoginIdAttribute was added
// they need IdAttribute == LoginIdAttribute not to break
if s.LoginIdAttribute == nil {
s.LoginIdAttribute = s.IdAttribute
}
if s.SyncIntervalMinutes == nil { if s.SyncIntervalMinutes == nil {
s.SyncIntervalMinutes = NewInt(60) s.SyncIntervalMinutes = NewInt(60)
} }
@@ -2074,6 +2081,10 @@ func (ls *LdapSettings) isValid() *AppError {
if *ls.IdAttribute == "" { if *ls.IdAttribute == "" {
return NewAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "", http.StatusBadRequest) return NewAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "", http.StatusBadRequest)
} }
if *ls.LoginIdAttribute == "" {
return NewAppError("Config.IsValid", "model.config.is_valid.ldap_login_id", nil, "", http.StatusBadRequest)
}
} }
return nil return nil

View File

@@ -15,7 +15,7 @@ type SwitchRequest struct {
Password string `json:"password"` Password string `json:"password"`
NewPassword string `json:"new_password"` NewPassword string `json:"new_password"`
MfaCode string `json:"mfa_code"` MfaCode string `json:"mfa_code"`
LdapId string `json:"ldap_id"` LdapLoginId string `json:"ldap_id"`
} }
func (o *SwitchRequest) ToJson() string { func (o *SwitchRequest) ToJson() string {

View File

@@ -819,13 +819,12 @@ func (us SqlUserStore) GetByUsername(username string) store.StoreChannel {
}) })
} }
func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) store.StoreChannel { func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
params := map[string]interface{}{ params := map[string]interface{}{
"LoginId": loginId, "LoginId": loginId,
"AllowSignInWithUsername": allowSignInWithUsername, "AllowSignInWithUsername": allowSignInWithUsername,
"AllowSignInWithEmail": allowSignInWithEmail, "AllowSignInWithEmail": allowSignInWithEmail,
"LdapEnabled": ldapEnabled,
} }
users := []*model.User{} users := []*model.User{}
@@ -837,8 +836,7 @@ func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allo
Users Users
WHERE WHERE
(:AllowSignInWithUsername AND Username = :LoginId) (:AllowSignInWithUsername AND Username = :LoginId)
OR (:AllowSignInWithEmail AND Email = :LoginId) OR (:AllowSignInWithEmail AND Email = :LoginId)`,
OR (:LdapEnabled AND AuthService = '`+model.USER_AUTH_SERVICE_LDAP+`' AND AuthData = :LoginId)`,
params); err != nil { params); err != nil {
result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if len(users) == 1 { } else if len(users) == 1 {

View File

@@ -228,7 +228,7 @@ type UserStore interface {
GetByAuth(authData *string, authService string) StoreChannel GetByAuth(authData *string, authService string) StoreChannel
GetAllUsingAuthService(authService string) StoreChannel GetAllUsingAuthService(authService string) StoreChannel
GetByUsername(username string) StoreChannel GetByUsername(username string) StoreChannel
GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) StoreChannel GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail bool) StoreChannel
VerifyEmail(userId string) StoreChannel VerifyEmail(userId string) StoreChannel
GetEtagForAllProfiles() StoreChannel GetEtagForAllProfiles() StoreChannel
GetEtagForProfiles(teamId string) StoreChannel GetEtagForProfiles(teamId string) StoreChannel

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.
@@ -258,13 +258,13 @@ func (_m *UserStore) GetEtagForProfilesNotInTeam(teamId string) store.StoreChann
return r0 return r0
} }
// GetForLogin provides a mock function with given fields: loginId, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled // GetForLogin provides a mock function with given fields: loginId, allowSignInWithUsername, allowSignInWithEmail
func (_m *UserStore) GetForLogin(loginId string, allowSignInWithUsername bool, allowSignInWithEmail bool, ldapEnabled bool) store.StoreChannel { func (_m *UserStore) GetForLogin(loginId string, allowSignInWithUsername bool, allowSignInWithEmail bool) store.StoreChannel {
ret := _m.Called(loginId, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled) ret := _m.Called(loginId, allowSignInWithUsername, allowSignInWithEmail)
var r0 store.StoreChannel var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, bool, bool, bool) store.StoreChannel); ok { if rf, ok := ret.Get(0).(func(string, bool, bool) store.StoreChannel); ok {
r0 = rf(loginId, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled) r0 = rf(loginId, allowSignInWithUsername, allowSignInWithEmail)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel) r0 = ret.Get(0).(store.StoreChannel)

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v1.0.0 // Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`. // Regenerate this file using `make store-mocks`.

View File

@@ -1091,64 +1091,26 @@ func testUserStoreGetForLogin(t *testing.T, ss store.Store) {
} }
store.Must(ss.User().Save(u2)) store.Must(ss.User().Save(u2))
if result := <-ss.User().GetForLogin(u1.Username, true, true, true); result.Err != nil { if result := <-ss.User().GetForLogin(u1.Username, true, true); result.Err != nil {
t.Fatal("Should have gotten user by username", result.Err) t.Fatal("Should have gotten user by username", result.Err)
} else if result.Data.(*model.User).Id != u1.Id { } else if result.Data.(*model.User).Id != u1.Id {
t.Fatal("Should have gotten user1 by username") t.Fatal("Should have gotten user1 by username")
} }
if result := <-ss.User().GetForLogin(u1.Email, true, true, true); result.Err != nil { if result := <-ss.User().GetForLogin(u1.Email, true, true); result.Err != nil {
t.Fatal("Should have gotten user by email", result.Err) t.Fatal("Should have gotten user by email", result.Err)
} else if result.Data.(*model.User).Id != u1.Id { } else if result.Data.(*model.User).Id != u1.Id {
t.Fatal("Should have gotten user1 by email") t.Fatal("Should have gotten user1 by email")
} }
if result := <-ss.User().GetForLogin(*u2.AuthData, true, true, true); result.Err != nil {
t.Fatal("Should have gotten user by AD/LDAP AuthData", result.Err)
} else if result.Data.(*model.User).Id != u2.Id {
t.Fatal("Should have gotten user2 by AD/LDAP AuthData")
}
// prevent getting user by AuthData when they're not an LDAP user
if result := <-ss.User().GetForLogin(*u1.AuthData, true, true, true); result.Err == nil {
t.Fatal("Should not have gotten user by non-AD/LDAP AuthData")
}
// prevent getting user when different login methods are disabled // prevent getting user when different login methods are disabled
if result := <-ss.User().GetForLogin(u1.Username, false, true, true); result.Err == nil { if result := <-ss.User().GetForLogin(u1.Username, false, true); result.Err == nil {
t.Fatal("Should have failed to get user1 by username") t.Fatal("Should have failed to get user1 by username")
} }
if result := <-ss.User().GetForLogin(u1.Email, true, false, true); result.Err == nil { if result := <-ss.User().GetForLogin(u1.Email, true, false); result.Err == nil {
t.Fatal("Should have failed to get user1 by email") t.Fatal("Should have failed to get user1 by email")
} }
if result := <-ss.User().GetForLogin(*u2.AuthData, true, true, false); result.Err == nil {
t.Fatal("Should have failed to get user3 by AD/LDAP AuthData")
}
auth3 := model.NewId()
// test a special case where two users will have conflicting login information so we throw a special error
u3 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_LDAP,
AuthData: &auth3,
}
store.Must(ss.User().Save(u3))
u4 := &model.User{
Email: model.NewId(),
Username: model.NewId(),
AuthService: model.USER_AUTH_SERVICE_LDAP,
AuthData: &u3.Username,
}
store.Must(ss.User().Save(u4))
if err := (<-ss.User().GetForLogin(u3.Username, true, true, true)).Err; err == nil {
t.Fatal("Should have failed to get users with conflicting login information")
}
} }
func testUserStoreUpdatePassword(t *testing.T, ss store.Store) { func testUserStoreUpdatePassword(t *testing.T, ss store.Store) {