diff --git a/app/diagnostics.go b/app/diagnostics.go index c7d9f8b84b..320ef8e54f 100644 --- a/app/diagnostics.go +++ b/app/diagnostics.go @@ -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), diff --git a/app/user.go b/app/user.go index d3c7f46caa..8f32e5e02d 100644 --- a/app/user.go +++ b/app/user.go @@ -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 } diff --git a/i18n/en.json b/i18n/en.json index 636eb02312..0c5e715198 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -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://." diff --git a/model/config.go b/model/config.go index bc2e5757e4..083c0e3cf8 100644 --- a/model/config.go +++ b/model/config.go @@ -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 diff --git a/model/config_test.go b/model/config_test.go index 8927d6ff7f..2aaff55577 100644 --- a/model/config_test.go +++ b/model/config_test.go @@ -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() diff --git a/model/user.go b/model/user.go index 6092afb9a2..e9322acfbb 100644 --- a/model/user.go +++ b/model/user.go @@ -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 {