MM-28090 User settings api when ldap sync (#16822)

Automatic Merge
Этот коммит содержится в:
Max Erenberg
2021-03-22 14:02:16 -04:00
коммит произвёл GitHub
родитель 4aac52bced
Коммит 6a77e24adc
12 изменённых файлов: 395 добавлений и 3 удалений

Просмотреть файл

@@ -517,6 +517,72 @@ func (th *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
return ruser
}
func (th *TestHelper) CreateUserWithAuth(authService string) *model.User {
id := model.NewId()
user := &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + id,
Nickname: "nn_" + id,
EmailVerified: true,
AuthService: authService,
}
user, err := th.App.CreateUser(user)
if err != nil {
panic(err)
}
return user
}
func (th *TestHelper) SetupLdapConfig() {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableMultifactorAuthentication = true
*cfg.LdapSettings.Enable = true
*cfg.LdapSettings.EnableSync = true
*cfg.LdapSettings.LdapServer = "dockerhost"
*cfg.LdapSettings.BaseDN = "dc=mm,dc=test,dc=com"
*cfg.LdapSettings.BindUsername = "cn=admin,dc=mm,dc=test,dc=com"
*cfg.LdapSettings.BindPassword = "mostest"
*cfg.LdapSettings.FirstNameAttribute = "cn"
*cfg.LdapSettings.LastNameAttribute = "sn"
*cfg.LdapSettings.NicknameAttribute = "cn"
*cfg.LdapSettings.EmailAttribute = "mail"
*cfg.LdapSettings.UsernameAttribute = "uid"
*cfg.LdapSettings.IdAttribute = "cn"
*cfg.LdapSettings.LoginIdAttribute = "uid"
*cfg.LdapSettings.SkipCertificateVerification = true
*cfg.LdapSettings.GroupFilter = ""
*cfg.LdapSettings.GroupDisplayNameAttribute = "cN"
*cfg.LdapSettings.GroupIdAttribute = "entRyUuId"
*cfg.LdapSettings.MaxPageSize = 0
})
th.App.Srv().SetLicense(model.NewTestLicense("ldap"))
}
func (th *TestHelper) SetupSamlConfig() {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.SamlSettings.Enable = true
*cfg.SamlSettings.Verify = false
*cfg.SamlSettings.Encrypt = false
*cfg.SamlSettings.IdpUrl = "https://does.notmatter.com"
*cfg.SamlSettings.IdpDescriptorUrl = "https://localhost/adfs/services/trust"
*cfg.SamlSettings.AssertionConsumerServiceURL = "https://localhost/login/sso/saml"
*cfg.SamlSettings.ServiceProviderIdentifier = "https://localhost/login/sso/saml"
*cfg.SamlSettings.IdpCertificateFile = app.SamlIdpCertificateName
*cfg.SamlSettings.PrivateKeyFile = app.SamlPrivateKeyName
*cfg.SamlSettings.PublicCertificateFile = app.SamlPublicCertificateName
*cfg.SamlSettings.EmailAttribute = "Email"
*cfg.SamlSettings.UsernameAttribute = "Username"
*cfg.SamlSettings.FirstNameAttribute = "FirstName"
*cfg.SamlSettings.LastNameAttribute = "LastName"
*cfg.SamlSettings.NicknameAttribute = ""
*cfg.SamlSettings.PositionAttribute = ""
*cfg.SamlSettings.LocaleAttribute = ""
*cfg.SamlSettings.SignatureAlgorithm = model.SAML_SETTINGS_SIGNATURE_ALGORITHM_SHA256
*cfg.SamlSettings.CanonicalAlgorithm = model.SAML_SETTINGS_CANONICAL_ALGORITHM_C14N11
})
th.App.Srv().SetLicense(model.NewTestLicense("saml"))
}
func (th *TestHelper) CreatePublicChannel() *model.Channel {
return th.CreateChannelWithClient(th.Client, model.CHANNEL_OPEN)
}

Просмотреть файл

@@ -466,8 +466,19 @@ func setProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("filename", imageArray[0].Filename)
}
if user, err := c.App.GetUser(c.Params.UserId); err == nil {
auditRec.AddMeta("user", user)
user, err := c.App.GetUser(c.Params.UserId)
if err != nil {
c.SetInvalidUrlParam("user_id")
return
}
auditRec.AddMeta("user", user)
if (user.IsLDAPUser() || (user.IsSAMLUser() && *c.App.Config().SamlSettings.EnableSyncWithLdap)) &&
*c.App.Config().LdapSettings.PictureAttribute != "" {
c.Err = model.NewAppError(
"uploadProfileImage", "api.user.upload_profile_user.login_provider_attribute_set.app_error",
nil, "", http.StatusConflict)
return
}
imageData := imageArray[0]
@@ -1109,6 +1120,15 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
// Check that the fields being updated are not set by the login provider
conflictField := c.App.CheckProviderAttributes(ouser, user.ToPatch())
if conflictField != "" {
c.Err = model.NewAppError(
"updateUser", "api.user.update_user.login_provider_attribute_set.app_error",
map[string]interface{}{"Field": conflictField}, "", http.StatusConflict)
return
}
// If eMail update is attempted by the currently logged in user, check if correct password was provided
if user.Email != "" && ouser.Email != user.Email && c.App.Session().UserId == c.Params.UserId {
err = c.App.DoubleCheckPassword(ouser, user.Password)
@@ -1172,6 +1192,14 @@ func patchUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
conflictField := c.App.CheckProviderAttributes(ouser, patch)
if conflictField != "" {
c.Err = model.NewAppError(
"patchUser", "api.user.patch_user.login_provider_attribute_set.app_error",
map[string]interface{}{"Field": conflictField}, "", http.StatusConflict)
return
}
// If eMail update is attempted by the currently logged in user, check if correct password was provided
if patch.Email != nil && ouser.Email != *patch.Email && c.App.Session().UserId == c.Params.UserId {
if patch.Password == nil {

Просмотреть файл

@@ -14,9 +14,11 @@ import (
"github.com/dgryski/dgoogauth"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v5/app"
"github.com/mattermost/mattermost-server/v5/einterfaces/mocks"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/shared/mail"
"github.com/mattermost/mattermost-server/v5/utils/testutils"
@@ -5958,3 +5960,175 @@ func TestReadThreads(t *testing.T) {
require.Equal(t, uss3.Threads[0].LastViewedAt, timestamp)
})
}
func TestPatchAndUpdateWithProviderAttributes(t *testing.T) {
t.Run("LDAP user", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
user := th.CreateUserWithAuth(model.USER_AUTH_SERVICE_LDAP)
ldapMock := &mocks.LdapInterface{}
ldapMock.Mock.On(
"CheckProviderAttributes",
mock.Anything, // app.AppIface
mock.Anything, // *model.User
mock.Anything, // *model.Patch
).Return("")
th.App.Srv().Ldap = ldapMock
// CheckProviderAttributes should be called for both Patch and Update
th.SystemAdminClient.PatchUser(user.Id, &model.UserPatch{})
ldapMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 1)
th.SystemAdminClient.UpdateUser(user)
ldapMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 2)
})
t.Run("SAML user", func(t *testing.T) {
t.Run("with LDAP sync", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
th.SetupLdapConfig()
th.SetupSamlConfig()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.SamlSettings.EnableSyncWithLdap = true
})
user := th.CreateUserWithAuth(model.USER_AUTH_SERVICE_SAML)
ldapMock := &mocks.LdapInterface{}
ldapMock.Mock.On(
"CheckProviderAttributes", mock.Anything, mock.Anything, mock.Anything,
).Return("")
th.App.Srv().Ldap = ldapMock
th.SystemAdminClient.PatchUser(user.Id, &model.UserPatch{})
ldapMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 1)
th.SystemAdminClient.UpdateUser(user)
ldapMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 2)
})
t.Run("without LDAP sync", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
user := th.CreateUserWithAuth(model.USER_AUTH_SERVICE_SAML)
samlMock := &mocks.SamlInterface{}
samlMock.Mock.On(
"CheckProviderAttributes", mock.Anything, mock.Anything, mock.Anything,
).Return("")
th.App.Srv().Saml = samlMock
th.SystemAdminClient.PatchUser(user.Id, &model.UserPatch{})
samlMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 1)
th.SystemAdminClient.UpdateUser(user)
samlMock.AssertNumberOfCalls(t, "CheckProviderAttributes", 2)
})
})
t.Run("OpenID user", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
user := th.CreateUserWithAuth(model.SERVICE_OPENID)
// OAUTH users cannot change these fields
for _, fieldName := range []string{
"FirstName",
"LastName",
} {
patch := user.ToPatch()
patch.SetField(fieldName, "something new")
conflictField := th.App.CheckProviderAttributes(user, patch)
require.NotEqual(t, "", conflictField)
}
})
t.Run("Patch username", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
// For non-email users, the username must be changed through the provider
for _, authService := range []string{
model.USER_AUTH_SERVICE_LDAP,
model.USER_AUTH_SERVICE_SAML,
model.SERVICE_OPENID,
} {
user := th.CreateUserWithAuth(authService)
patch := &model.UserPatch{Username: model.NewString("something new")}
conflictField := th.App.CheckProviderAttributes(user, patch)
require.NotEqual(t, "", conflictField)
}
})
}
func TestSetProfileImageWithProviderAttributes(t *testing.T) {
data, err := testutils.ReadTestFile("test.png")
require.NoError(t, err)
type imageTestCase struct {
testName string
ldapAttrIsSet bool
shouldPass bool
}
doImageTest := func(t *testing.T, th *TestHelper, user *model.User, testCase imageTestCase) {
client := th.SystemAdminClient
t.Run(testCase.testName, func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
if testCase.ldapAttrIsSet {
*cfg.LdapSettings.PictureAttribute = "jpegPhoto"
} else {
*cfg.LdapSettings.PictureAttribute = ""
}
})
ok, resp := client.SetProfileImage(user.Id, data)
if testCase.shouldPass {
require.True(t, ok)
CheckNoError(t, resp)
} else {
require.False(t, ok)
checkHTTPStatus(t, resp, http.StatusConflict, true)
}
})
}
doCleanup := func(t *testing.T, th *TestHelper, user *model.User) {
info := &model.FileInfo{Path: "users/" + user.Id + "/profile.png"}
err = th.cleanupTestFile(info)
require.Nil(t, err)
}
t.Run("LDAP user", func(t *testing.T) {
testCases := []imageTestCase{
{"profile picture attribute is set", true, false},
{"profile picture attribute is not set", false, true},
}
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
th.SetupLdapConfig()
user := th.CreateUserWithAuth(model.USER_AUTH_SERVICE_LDAP)
for _, testCase := range testCases {
doImageTest(t, th, user, testCase)
}
doCleanup(t, th, user)
})
t.Run("SAML user", func(t *testing.T) {
th := SetupEnterprise(t).InitBasic()
defer th.TearDown()
th.SetupLdapConfig()
th.SetupSamlConfig()
user := th.CreateUserWithAuth(model.USER_AUTH_SERVICE_SAML)
t.Run("with LDAP sync", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.SamlSettings.EnableSyncWithLdap = true
})
testCases := []imageTestCase{
{"profile picture attribute is set", true, false},
{"profile picture attribute is not set", false, true},
}
for _, testCase := range testCases {
doImageTest(t, th, user, testCase)
}
})
t.Run("without LDAP sync", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.SamlSettings.EnableSyncWithLdap = false
})
testCases := []imageTestCase{
{"profile picture attribute is set", true, true},
{"profile picture attribute is not set", false, true},
}
for _, testCase := range testCases {
doImageTest(t, th, user, testCase)
}
})
doCleanup(t, th, user)
})
}