[MM-20061] Add System Admin filter to both LDAP and SAML (#13534)
* promote user to admin upon login * Add SAML support for admin accounts * update en.json * update i18n * Add tests as per comments * change function name * fix config.go * invalidate cache so its not checking for cache when roles change * add enable attribute and filter
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5f7ca55b13
Коммит
dc24c9abe8
@@ -471,6 +471,7 @@ func (a *App) trackConfig() {
|
||||
a.SendDiagnostic(TRACK_CONFIG_LDAP, map[string]interface{}{
|
||||
"enable": *cfg.LdapSettings.Enable,
|
||||
"enable_sync": *cfg.LdapSettings.EnableSync,
|
||||
"enable_admin_filter": *cfg.LdapSettings.EnableAdminFilter,
|
||||
"connection_security": *cfg.LdapSettings.ConnectionSecurity,
|
||||
"skip_certificate_verification": *cfg.LdapSettings.SkipCertificateVerification,
|
||||
"sync_interval_minutes": *cfg.LdapSettings.SyncIntervalMinutes,
|
||||
@@ -492,6 +493,7 @@ func (a *App) trackConfig() {
|
||||
"isdefault_group_display_name_attribute": isDefault(*cfg.LdapSettings.GroupDisplayNameAttribute, model.LDAP_SETTINGS_DEFAULT_GROUP_DISPLAY_NAME_ATTRIBUTE),
|
||||
"isdefault_group_id_attribute": isDefault(*cfg.LdapSettings.GroupIdAttribute, model.LDAP_SETTINGS_DEFAULT_GROUP_ID_ATTRIBUTE),
|
||||
"isempty_guest_filter": isDefault(*cfg.LdapSettings.GuestFilter, ""),
|
||||
"isempty_admin_filter": isDefault(*cfg.LdapSettings.AdminFilter, ""),
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_COMPLIANCE, map[string]interface{}{
|
||||
@@ -509,6 +511,7 @@ func (a *App) trackConfig() {
|
||||
"enable": *cfg.SamlSettings.Enable,
|
||||
"enable_sync_with_ldap": *cfg.SamlSettings.EnableSyncWithLdap,
|
||||
"enable_sync_with_ldap_include_auth": *cfg.SamlSettings.EnableSyncWithLdapIncludeAuth,
|
||||
"enable_admin_attribute": *cfg.SamlSettings.EnableAdminAttribute,
|
||||
"verify": *cfg.SamlSettings.Verify,
|
||||
"encrypt": *cfg.SamlSettings.Encrypt,
|
||||
"sign_request": *cfg.SamlSettings.SignRequest,
|
||||
@@ -518,6 +521,7 @@ func (a *App) trackConfig() {
|
||||
"isdefault_scoping_idp_name": isDefault(*cfg.SamlSettings.ScopingIDPName, ""),
|
||||
"isdefault_id_attribute": isDefault(*cfg.SamlSettings.IdAttribute, model.SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE),
|
||||
"isdefault_guest_attribute": isDefault(*cfg.SamlSettings.GuestAttribute, model.SAML_SETTINGS_DEFAULT_GUEST_ATTRIBUTE),
|
||||
"isdefault_admin_attribute": isDefault(*cfg.SamlSettings.AdminAttribute, model.SAML_SETTINGS_DEFAULT_ADMIN_ATTRIBUTE),
|
||||
"isdefault_first_name_attribute": isDefault(*cfg.SamlSettings.FirstNameAttribute, model.SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE),
|
||||
"isdefault_last_name_attribute": isDefault(*cfg.SamlSettings.LastNameAttribute, model.SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE),
|
||||
"isdefault_email_attribute": isDefault(*cfg.SamlSettings.EmailAttribute, model.SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE),
|
||||
|
||||
@@ -1435,6 +1435,7 @@ func (a *App) UpdateUserRoles(userId string, newRoles string, sendWebSocketEvent
|
||||
mlog.Error("Failed during updating user roles", mlog.Err(result.Err))
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
a.ClearSessionCacheForUser(user.Id)
|
||||
|
||||
if sendWebSocketEvent {
|
||||
@@ -2313,6 +2314,8 @@ func (a *App) PromoteGuestToUser(user *model.User, requestorId string) *model.Ap
|
||||
}
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
a.ClearSessionCacheForUser(user.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2354,6 +2357,9 @@ func (a *App) DemoteUserToGuest(user *model.User) *model.AppError {
|
||||
}
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(user.Id)
|
||||
a.ClearSessionCacheForUser(user.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4118,6 +4118,10 @@
|
||||
"id": "ent.ldap.syncronize.search_failure_size_exceeded.app_error",
|
||||
"translation": "Size Limit Exceeded. Try checking your [max page size](https://docs.mattermost.com/deployment/sso-ldap.html#i-see-the-log-error-ldap-result-code-4-size-limit-exceeded)."
|
||||
},
|
||||
{
|
||||
"id": "ent.ldap.validate_admin_filter.app_error",
|
||||
"translation": "Invalid AD/LDAP Admin Filter"
|
||||
},
|
||||
{
|
||||
"id": "ent.ldap.validate_filter.app_error",
|
||||
"translation": "Invalid AD/LDAP Filter"
|
||||
@@ -4878,6 +4882,10 @@
|
||||
"id": "model.config.is_valid.restrict_direct_message.app_error",
|
||||
"translation": "Invalid direct message restriction. Must be 'any', or 'team'"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.saml_admin_attribute.app_error",
|
||||
"translation": "Invalid Admin attribute. Must be in the form 'field=value'"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.saml_assertion_consumer_service_url.app_error",
|
||||
"translation": "Service Provider Login URL must be a valid URL and start with http:// or https://."
|
||||
|
||||
@@ -132,6 +132,7 @@ const (
|
||||
|
||||
SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_GUEST_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_ADMIN_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
|
||||
SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
|
||||
@@ -1700,9 +1701,11 @@ type LdapSettings struct {
|
||||
BindPassword *string
|
||||
|
||||
// Filtering
|
||||
UserFilter *string
|
||||
GroupFilter *string
|
||||
GuestFilter *string
|
||||
UserFilter *string
|
||||
GroupFilter *string
|
||||
GuestFilter *string
|
||||
EnableAdminFilter *bool
|
||||
AdminFilter *string
|
||||
|
||||
// Group Mapping
|
||||
GroupDisplayNameAttribute *string
|
||||
@@ -1746,6 +1749,10 @@ func (s *LdapSettings) SetDefaults() {
|
||||
s.EnableSync = NewBool(*s.Enable)
|
||||
}
|
||||
|
||||
if s.EnableAdminFilter == nil {
|
||||
s.EnableAdminFilter = NewBool(false)
|
||||
}
|
||||
|
||||
if s.LdapServer == nil {
|
||||
s.LdapServer = NewString("")
|
||||
}
|
||||
@@ -1778,6 +1785,10 @@ func (s *LdapSettings) SetDefaults() {
|
||||
s.GuestFilter = NewString("")
|
||||
}
|
||||
|
||||
if s.AdminFilter == nil {
|
||||
s.AdminFilter = NewString("")
|
||||
}
|
||||
|
||||
if s.GroupFilter == nil {
|
||||
s.GroupFilter = NewString("")
|
||||
}
|
||||
@@ -1927,15 +1938,17 @@ type SamlSettings struct {
|
||||
PrivateKeyFile *string
|
||||
|
||||
// User Mapping
|
||||
IdAttribute *string
|
||||
GuestAttribute *string
|
||||
FirstNameAttribute *string
|
||||
LastNameAttribute *string
|
||||
EmailAttribute *string
|
||||
UsernameAttribute *string
|
||||
NicknameAttribute *string
|
||||
LocaleAttribute *string
|
||||
PositionAttribute *string
|
||||
IdAttribute *string
|
||||
GuestAttribute *string
|
||||
EnableAdminAttribute *bool
|
||||
AdminAttribute *string
|
||||
FirstNameAttribute *string
|
||||
LastNameAttribute *string
|
||||
EmailAttribute *string
|
||||
UsernameAttribute *string
|
||||
NicknameAttribute *string
|
||||
LocaleAttribute *string
|
||||
PositionAttribute *string
|
||||
|
||||
LoginButtonText *string
|
||||
|
||||
@@ -1957,6 +1970,10 @@ func (s *SamlSettings) SetDefaults() {
|
||||
s.EnableSyncWithLdapIncludeAuth = NewBool(false)
|
||||
}
|
||||
|
||||
if s.EnableAdminAttribute == nil {
|
||||
s.EnableAdminAttribute = NewBool(false)
|
||||
}
|
||||
|
||||
if s.Verify == nil {
|
||||
s.Verify = NewBool(true)
|
||||
}
|
||||
@@ -2024,6 +2041,9 @@ func (s *SamlSettings) SetDefaults() {
|
||||
if s.GuestAttribute == nil {
|
||||
s.GuestAttribute = NewString(SAML_SETTINGS_DEFAULT_GUEST_ATTRIBUTE)
|
||||
}
|
||||
if s.AdminAttribute == nil {
|
||||
s.AdminAttribute = NewString(SAML_SETTINGS_DEFAULT_ADMIN_ATTRIBUTE)
|
||||
}
|
||||
if s.FirstNameAttribute == nil {
|
||||
s.FirstNameAttribute = NewString(SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE)
|
||||
}
|
||||
@@ -2816,6 +2836,12 @@ func (s *LdapSettings) isValid() *AppError {
|
||||
return NewAppError("LdapSettings.isValid", "ent.ldap.validate_guest_filter.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
if *s.AdminFilter != "" {
|
||||
if _, err := ldap.CompileFilter(*s.AdminFilter); err != nil {
|
||||
return NewAppError("LdapSettings.isValid", "ent.ldap.validate_admin_filter.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -2878,6 +2904,15 @@ func (s *SamlSettings) isValid() *AppError {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.saml_guest_attribute.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*s.AdminAttribute) > 0 {
|
||||
if !(strings.Contains(*s.AdminAttribute, "=")) {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.saml_admin_attribute.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
if len(strings.Split(*s.AdminAttribute, "=")) != 2 {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.saml_admin_attribute.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -171,6 +171,21 @@ func TestConfigOverwriteGuestSettings(t *testing.T) {
|
||||
require.Equal(t, *c1.SamlSettings.GuestAttribute, attribute)
|
||||
}
|
||||
|
||||
func TestConfigOverwriteAdminSettings(t *testing.T) {
|
||||
const attribute = "FakeAttributeName"
|
||||
c1 := Config{
|
||||
SamlSettings: SamlSettings{
|
||||
AdminAttribute: NewString(attribute),
|
||||
},
|
||||
}
|
||||
|
||||
c1.SetDefaults()
|
||||
|
||||
if *c1.SamlSettings.AdminAttribute != attribute {
|
||||
t.Fatal("SamlSettings.AdminAttribute should be overwritten")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDefaultServiceSettingsExperimentalGroupUnreadChannels(t *testing.T) {
|
||||
c1 := Config{}
|
||||
c1.SetDefaults()
|
||||
@@ -1069,6 +1084,105 @@ func TestLdapSettingsIsValid(t *testing.T) {
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "valid Admin filter #1",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("(property=value)"),
|
||||
},
|
||||
ExpectError: false,
|
||||
},
|
||||
{
|
||||
Name: "invalid Admin filter #1",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("("),
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Name: "invalid Admin filter #2",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("()"),
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Name: "valid Admin filter #2",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("(&(property=value)(otherthing=othervalue))"),
|
||||
},
|
||||
ExpectError: false,
|
||||
},
|
||||
{
|
||||
Name: "valid Admin filter #3",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("(&(property=value)(|(otherthing=othervalue)(other=thing)))"),
|
||||
},
|
||||
ExpectError: false,
|
||||
},
|
||||
{
|
||||
Name: "invalid Admin filter #3",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("(&(property=value)(|(otherthing=othervalue)(other=thing))"),
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
{
|
||||
Name: "invalid Admin filter #4",
|
||||
LdapSettings: LdapSettings{
|
||||
Enable: NewBool(true),
|
||||
LdapServer: NewString("server"),
|
||||
BaseDN: NewString("basedn"),
|
||||
EmailAttribute: NewString("email"),
|
||||
UsernameAttribute: NewString("username"),
|
||||
IdAttribute: NewString("id"),
|
||||
LoginIdAttribute: NewString("loginid"),
|
||||
AdminFilter: NewString("(&(property=value)((otherthing=othervalue)(other=thing)))"),
|
||||
},
|
||||
ExpectError: true,
|
||||
},
|
||||
} {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
test.LdapSettings.SetDefaults()
|
||||
|
||||
@@ -633,6 +633,10 @@ func (u *User) IsGuest() bool {
|
||||
return IsInRole(u.Roles, SYSTEM_GUEST_ROLE_ID)
|
||||
}
|
||||
|
||||
func (u *User) IsSystemAdmin() bool {
|
||||
return IsInRole(u.Roles, SYSTEM_ADMIN_ROLE_ID)
|
||||
}
|
||||
|
||||
// Make sure you acually want to use this function. In context.go there are functions to check permissions
|
||||
// This function should not be used to check permissions.
|
||||
func (u *User) IsInRole(inRole string) bool {
|
||||
|
||||
Ссылка в новой задаче
Block a user