* Add restricted user filter to LDAP authentification * Fix unit test casestags/v1.21.12.1
| @@ -61,6 +61,10 @@ var ( | |||||
| Name: "admin-filter", | Name: "admin-filter", | ||||
| Usage: "An LDAP filter specifying if a user should be given administrator privileges.", | Usage: "An LDAP filter specifying if a user should be given administrator privileges.", | ||||
| }, | }, | ||||
| cli.StringFlag{ | |||||
| Name: "restricted-filter", | |||||
| Usage: "An LDAP filter specifying if a user should be given restricted status.", | |||||
| }, | |||||
| cli.BoolFlag{ | cli.BoolFlag{ | ||||
| Name: "allow-deactivate-all", | Name: "allow-deactivate-all", | ||||
| Usage: "Allow empty search results to deactivate all users.", | Usage: "Allow empty search results to deactivate all users.", | ||||
| @@ -235,6 +239,9 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error { | |||||
| if c.IsSet("admin-filter") { | if c.IsSet("admin-filter") { | ||||
| config.Source.AdminFilter = c.String("admin-filter") | config.Source.AdminFilter = c.String("admin-filter") | ||||
| } | } | ||||
| if c.IsSet("restricted-filter") { | |||||
| config.Source.RestrictedFilter = c.String("restricted-filter") | |||||
| } | |||||
| if c.IsSet("allow-deactivate-all") { | if c.IsSet("allow-deactivate-all") { | ||||
| config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all") | config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all") | ||||
| } | } | ||||
| @@ -39,6 +39,7 @@ func TestAddLdapBindDn(t *testing.T) { | |||||
| "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", | "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", | ||||
| "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", | |||||
| "--username-attribute", "uid-bind full", | "--username-attribute", "uid-bind full", | ||||
| "--firstname-attribute", "givenName-bind full", | "--firstname-attribute", "givenName-bind full", | ||||
| "--surname-attribute", "sn-bind full", | "--surname-attribute", "sn-bind full", | ||||
| @@ -74,6 +75,7 @@ func TestAddLdapBindDn(t *testing.T) { | |||||
| SearchPageSize: 99, | SearchPageSize: 99, | ||||
| Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", | |||||
| Enabled: true, | Enabled: true, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -265,6 +267,7 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||
| "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", | "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", | ||||
| "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", | "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", | ||||
| "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | ||||
| "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", | |||||
| "--username-attribute", "uid-simple full", | "--username-attribute", "uid-simple full", | ||||
| "--firstname-attribute", "givenName-simple full", | "--firstname-attribute", "givenName-simple full", | ||||
| "--surname-attribute", "sn-simple full", | "--surname-attribute", "sn-simple full", | ||||
| @@ -292,6 +295,7 @@ func TestAddLdapSimpleAuth(t *testing.T) { | |||||
| AttributeSSHPublicKey: "publickey-simple full", | AttributeSSHPublicKey: "publickey-simple full", | ||||
| Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", | Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", | ||||
| AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | ||||
| RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", | |||||
| Enabled: true, | Enabled: true, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -499,6 +503,7 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||
| "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", | "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", | ||||
| "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", | |||||
| "--username-attribute", "uid-bind full", | "--username-attribute", "uid-bind full", | ||||
| "--firstname-attribute", "givenName-bind full", | "--firstname-attribute", "givenName-bind full", | ||||
| "--surname-attribute", "sn-bind full", | "--surname-attribute", "sn-bind full", | ||||
| @@ -543,6 +548,7 @@ func TestUpdateLdapBindDn(t *testing.T) { | |||||
| SearchPageSize: 99, | SearchPageSize: 99, | ||||
| Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", | ||||
| RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", | |||||
| Enabled: true, | Enabled: true, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -978,6 +984,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||
| "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", | "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", | ||||
| "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", | "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", | ||||
| "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | ||||
| "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", | |||||
| "--username-attribute", "uid-simple full", | "--username-attribute", "uid-simple full", | ||||
| "--firstname-attribute", "givenName-simple full", | "--firstname-attribute", "givenName-simple full", | ||||
| "--surname-attribute", "sn-simple full", | "--surname-attribute", "sn-simple full", | ||||
| @@ -1006,6 +1013,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { | |||||
| AttributeSSHPublicKey: "publickey-simple full", | AttributeSSHPublicKey: "publickey-simple full", | ||||
| Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", | Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", | ||||
| AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", | ||||
| RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -134,6 +134,7 @@ Admin operations: | |||||
| - `--user-search-base value`: The LDAP base at which user accounts will be searched for. Required. | - `--user-search-base value`: The LDAP base at which user accounts will be searched for. Required. | ||||
| - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required. | - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required. | ||||
| - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | ||||
| - `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status. | |||||
| - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | ||||
| - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | ||||
| - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | ||||
| @@ -158,6 +159,7 @@ Admin operations: | |||||
| - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | ||||
| - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. | - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. | ||||
| - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | ||||
| - `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status. | |||||
| - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | ||||
| - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | ||||
| - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | ||||
| @@ -182,6 +184,7 @@ Admin operations: | |||||
| - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | ||||
| - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required. | - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required. | ||||
| - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | ||||
| - `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status. | |||||
| - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | ||||
| - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | ||||
| - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | ||||
| @@ -202,6 +205,7 @@ Admin operations: | |||||
| - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | - `--user-search-base value`: The LDAP base at which user accounts will be searched for. | ||||
| - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. | - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. | ||||
| - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges. | ||||
| - `--restricted-filter value`: An LDAP filter specifying if a user should be given restricted status. | |||||
| - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name. | ||||
| - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name. | ||||
| - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname. | ||||
| @@ -313,4 +317,4 @@ var checklist = []check{ | |||||
| } | } | ||||
| ``` | ``` | ||||
| This function will receive a command line context and return a list of details about the problems or error. | |||||
| This function will receive a command line context and return a list of details about the problems or error. | |||||
| @@ -18,13 +18,14 @@ import ( | |||||
| ) | ) | ||||
| type ldapUser struct { | type ldapUser struct { | ||||
| UserName string | |||||
| Password string | |||||
| FullName string | |||||
| Email string | |||||
| OtherEmails []string | |||||
| IsAdmin bool | |||||
| SSHKeys []string | |||||
| UserName string | |||||
| Password string | |||||
| FullName string | |||||
| Email string | |||||
| OtherEmails []string | |||||
| IsAdmin bool | |||||
| IsRestricted bool | |||||
| SSHKeys []string | |||||
| } | } | ||||
| var gitLDAPUsers = []ldapUser{ | var gitLDAPUsers = []ldapUser{ | ||||
| @@ -55,10 +56,11 @@ var gitLDAPUsers = []ldapUser{ | |||||
| Email: "fry@planetexpress.com", | Email: "fry@planetexpress.com", | ||||
| }, | }, | ||||
| { | { | ||||
| UserName: "leela", | |||||
| Password: "leela", | |||||
| FullName: "Leela Turanga", | |||||
| Email: "leela@planetexpress.com", | |||||
| UserName: "leela", | |||||
| Password: "leela", | |||||
| FullName: "Leela Turanga", | |||||
| Email: "leela@planetexpress.com", | |||||
| IsRestricted: true, | |||||
| }, | }, | ||||
| { | { | ||||
| UserName: "bender", | UserName: "bender", | ||||
| @@ -109,6 +111,7 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) { | |||||
| "user_base": "ou=people,dc=planetexpress,dc=com", | "user_base": "ou=people,dc=planetexpress,dc=com", | ||||
| "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))", | "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))", | ||||
| "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)", | "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)", | ||||
| "restricted_filter": "(uid=leela)", | |||||
| "attribute_username": "uid", | "attribute_username": "uid", | ||||
| "attribute_name": "givenName", | "attribute_name": "givenName", | ||||
| "attribute_surname": "sn", | "attribute_surname": "sn", | ||||
| @@ -173,6 +176,11 @@ func TestLDAPUserSync(t *testing.T) { | |||||
| } else { | } else { | ||||
| assert.True(t, tds.Find("td:nth-child(5) i").HasClass("fa-square-o")) | assert.True(t, tds.Find("td:nth-child(5) i").HasClass("fa-square-o")) | ||||
| } | } | ||||
| if u.IsRestricted { | |||||
| assert.True(t, tds.Find("td:nth-child(6) i").HasClass("fa-check-square-o")) | |||||
| } else { | |||||
| assert.True(t, tds.Find("td:nth-child(6) i").HasClass("fa-square-o")) | |||||
| } | |||||
| } | } | ||||
| // Check if no users exist | // Check if no users exist | ||||
| @@ -475,13 +475,23 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| } | } | ||||
| if user != nil && | |||||
| !user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin { | |||||
| // Change existing admin flag only if AdminFilter option is set | |||||
| user.IsAdmin = sr.IsAdmin | |||||
| err = UpdateUserCols(user, "is_admin") | |||||
| if err != nil { | |||||
| return nil, err | |||||
| if user != nil && !user.ProhibitLogin { | |||||
| cols := make([]string, 0) | |||||
| if len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin { | |||||
| // Change existing admin flag only if AdminFilter option is set | |||||
| user.IsAdmin = sr.IsAdmin | |||||
| cols = append(cols, "is_admin") | |||||
| } | |||||
| if !user.IsAdmin && len(source.LDAP().RestrictedFilter) > 0 && user.IsRestricted != sr.IsRestricted { | |||||
| // Change existing restricted flag only if RestrictedFilter option is set | |||||
| user.IsRestricted = sr.IsRestricted | |||||
| cols = append(cols, "is_restricted") | |||||
| } | |||||
| if len(cols) > 0 { | |||||
| err = UpdateUserCols(user, cols...) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -504,15 +514,16 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use | |||||
| } | } | ||||
| user = &User{ | user = &User{ | ||||
| LowerName: strings.ToLower(sr.Username), | |||||
| Name: sr.Username, | |||||
| FullName: composeFullName(sr.Name, sr.Surname, sr.Username), | |||||
| Email: sr.Mail, | |||||
| LoginType: source.Type, | |||||
| LoginSource: source.ID, | |||||
| LoginName: login, | |||||
| IsActive: true, | |||||
| IsAdmin: sr.IsAdmin, | |||||
| LowerName: strings.ToLower(sr.Username), | |||||
| Name: sr.Username, | |||||
| FullName: composeFullName(sr.Name, sr.Surname, sr.Username), | |||||
| Email: sr.Mail, | |||||
| LoginType: source.Type, | |||||
| LoginSource: source.ID, | |||||
| LoginName: login, | |||||
| IsActive: true, | |||||
| IsAdmin: sr.IsAdmin, | |||||
| IsRestricted: sr.IsRestricted, | |||||
| } | } | ||||
| err := CreateUser(user) | err := CreateUser(user) | ||||
| @@ -1875,15 +1875,16 @@ func SyncExternalUsers(ctx context.Context) { | |||||
| log.Trace("SyncExternalUsers[%s]: Creating user %s", s.Name, su.Username) | log.Trace("SyncExternalUsers[%s]: Creating user %s", s.Name, su.Username) | ||||
| usr = &User{ | usr = &User{ | ||||
| LowerName: strings.ToLower(su.Username), | |||||
| Name: su.Username, | |||||
| FullName: fullName, | |||||
| LoginType: s.Type, | |||||
| LoginSource: s.ID, | |||||
| LoginName: su.Username, | |||||
| Email: su.Mail, | |||||
| IsAdmin: su.IsAdmin, | |||||
| IsActive: true, | |||||
| LowerName: strings.ToLower(su.Username), | |||||
| Name: su.Username, | |||||
| FullName: fullName, | |||||
| LoginType: s.Type, | |||||
| LoginSource: s.ID, | |||||
| LoginName: su.Username, | |||||
| Email: su.Mail, | |||||
| IsAdmin: su.IsAdmin, | |||||
| IsRestricted: su.IsRestricted, | |||||
| IsActive: true, | |||||
| } | } | ||||
| err = CreateUser(usr) | err = CreateUser(usr) | ||||
| @@ -1906,6 +1907,7 @@ func SyncExternalUsers(ctx context.Context) { | |||||
| // Check if user data has changed | // Check if user data has changed | ||||
| if (len(s.LDAP().AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) || | if (len(s.LDAP().AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) || | ||||
| (len(s.LDAP().RestrictedFilter) > 0 && usr.IsRestricted != su.IsRestricted) || | |||||
| !strings.EqualFold(usr.Email, su.Mail) || | !strings.EqualFold(usr.Email, su.Mail) || | ||||
| usr.FullName != fullName || | usr.FullName != fullName || | ||||
| !usr.IsActive { | !usr.IsActive { | ||||
| @@ -1918,9 +1920,13 @@ func SyncExternalUsers(ctx context.Context) { | |||||
| if len(s.LDAP().AdminFilter) > 0 { | if len(s.LDAP().AdminFilter) > 0 { | ||||
| usr.IsAdmin = su.IsAdmin | usr.IsAdmin = su.IsAdmin | ||||
| } | } | ||||
| // Change existing restricted flag only if RestrictedFilter option is set | |||||
| if !usr.IsAdmin && len(s.LDAP().RestrictedFilter) > 0 { | |||||
| usr.IsRestricted = su.IsRestricted | |||||
| } | |||||
| usr.IsActive = true | usr.IsActive = true | ||||
| err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_active") | |||||
| err = UpdateUserCols(usr, "full_name", "email", "is_admin", "is_restricted", "is_active") | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err) | log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", s.Name, usr.Name, err) | ||||
| } | } | ||||
| @@ -30,6 +30,7 @@ type AuthenticationForm struct { | |||||
| SearchPageSize int | SearchPageSize int | ||||
| Filter string | Filter string | ||||
| AdminFilter string | AdminFilter string | ||||
| RestrictedFilter string | |||||
| AllowDeactivateAll bool | AllowDeactivateAll bool | ||||
| IsActive bool | IsActive bool | ||||
| IsSyncEnabled bool | IsSyncEnabled bool | ||||
| @@ -46,6 +46,7 @@ type Source struct { | |||||
| SearchPageSize uint32 // Search with paging page size | SearchPageSize uint32 // Search with paging page size | ||||
| Filter string // Query filter to validate entry | Filter string // Query filter to validate entry | ||||
| AdminFilter string // Query filter to check if user is admin | AdminFilter string // Query filter to check if user is admin | ||||
| RestrictedFilter string // Query filter to check if user is restricted | |||||
| Enabled bool // if this source is disabled | Enabled bool // if this source is disabled | ||||
| AllowDeactivateAll bool // Allow an empty search response to deactivate all users from this source | AllowDeactivateAll bool // Allow an empty search response to deactivate all users from this source | ||||
| } | } | ||||
| @@ -58,6 +59,7 @@ type SearchResult struct { | |||||
| Mail string // E-mail address | Mail string // E-mail address | ||||
| SSHPublicKey []string // SSH Public Key | SSHPublicKey []string // SSH Public Key | ||||
| IsAdmin bool // if user is administrator | IsAdmin bool // if user is administrator | ||||
| IsRestricted bool // if user is restricted | |||||
| } | } | ||||
| func (ls *Source) sanitizedUserQuery(username string) (string, bool) { | func (ls *Source) sanitizedUserQuery(username string) (string, bool) { | ||||
| @@ -153,22 +155,48 @@ func bindUser(l *ldap.Conn, userDN, passwd string) error { | |||||
| } | } | ||||
| func checkAdmin(l *ldap.Conn, ls *Source, userDN string) bool { | func checkAdmin(l *ldap.Conn, ls *Source, userDN string) bool { | ||||
| if len(ls.AdminFilter) > 0 { | |||||
| log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN) | |||||
| search := ldap.NewSearchRequest( | |||||
| userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter, | |||||
| []string{ls.AttributeName}, | |||||
| nil) | |||||
| if len(ls.AdminFilter) == 0 { | |||||
| return false | |||||
| } | |||||
| log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN) | |||||
| search := ldap.NewSearchRequest( | |||||
| userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter, | |||||
| []string{ls.AttributeName}, | |||||
| nil) | |||||
| sr, err := l.Search(search) | |||||
| sr, err := l.Search(search) | |||||
| if err != nil { | |||||
| log.Error("LDAP Admin Search failed unexpectedly! (%v)", err) | |||||
| } else if len(sr.Entries) < 1 { | |||||
| log.Trace("LDAP Admin Search found no matching entries.") | |||||
| } else { | |||||
| return true | |||||
| } | |||||
| if err != nil { | |||||
| log.Error("LDAP Admin Search failed unexpectedly! (%v)", err) | |||||
| } else if len(sr.Entries) < 1 { | |||||
| log.Trace("LDAP Admin Search found no matching entries.") | |||||
| } else { | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool { | |||||
| if len(ls.RestrictedFilter) == 0 { | |||||
| return false | |||||
| } | |||||
| if ls.RestrictedFilter == "*" { | |||||
| return true | |||||
| } | |||||
| log.Trace("Checking restricted with filter %s and base %s", ls.RestrictedFilter, userDN) | |||||
| search := ldap.NewSearchRequest( | |||||
| userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.RestrictedFilter, | |||||
| []string{ls.AttributeName}, | |||||
| nil) | |||||
| sr, err := l.Search(search) | |||||
| if err != nil { | |||||
| log.Error("LDAP Restrictred Search failed unexpectedly! (%v)", err) | |||||
| } else if len(sr.Entries) < 1 { | |||||
| log.Trace("LDAP Restricted Search found no matching entries.") | |||||
| } else { | |||||
| return true | |||||
| } | } | ||||
| return false | return false | ||||
| } | } | ||||
| @@ -284,6 +312,10 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul | |||||
| sshPublicKey = sr.Entries[0].GetAttributeValues(ls.AttributeSSHPublicKey) | sshPublicKey = sr.Entries[0].GetAttributeValues(ls.AttributeSSHPublicKey) | ||||
| } | } | ||||
| isAdmin := checkAdmin(l, ls, userDN) | isAdmin := checkAdmin(l, ls, userDN) | ||||
| var isRestricted bool | |||||
| if !isAdmin { | |||||
| isRestricted = checkRestricted(l, ls, userDN) | |||||
| } | |||||
| if !directBind && ls.AttributesInBind { | if !directBind && ls.AttributesInBind { | ||||
| // binds user (checking password) after looking-up attributes in BindDN context | // binds user (checking password) after looking-up attributes in BindDN context | ||||
| @@ -300,6 +332,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul | |||||
| Mail: mail, | Mail: mail, | ||||
| SSHPublicKey: sshPublicKey, | SSHPublicKey: sshPublicKey, | ||||
| IsAdmin: isAdmin, | IsAdmin: isAdmin, | ||||
| IsRestricted: isRestricted, | |||||
| } | } | ||||
| } | } | ||||
| @@ -364,6 +397,9 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) { | |||||
| Mail: v.GetAttributeValue(ls.AttributeMail), | Mail: v.GetAttributeValue(ls.AttributeMail), | ||||
| IsAdmin: checkAdmin(l, ls, v.DN), | IsAdmin: checkAdmin(l, ls, v.DN), | ||||
| } | } | ||||
| if !result[i].IsAdmin { | |||||
| result[i].IsRestricted = checkRestricted(l, ls, v.DN) | |||||
| } | |||||
| if isAttributeSSHPublicKeySet { | if isAttributeSSHPublicKeySet { | ||||
| result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey) | result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey) | ||||
| } | } | ||||
| @@ -1893,6 +1893,8 @@ auths.use_paged_search = Use Paged Search | |||||
| auths.search_page_size = Page Size | auths.search_page_size = Page Size | ||||
| auths.filter = User Filter | auths.filter = User Filter | ||||
| auths.admin_filter = Admin Filter | auths.admin_filter = Admin Filter | ||||
| auths.restricted_filter = Restricted Filter | |||||
| auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ('*') to set all users that do not match Admin Filter as restricted. | |||||
| auths.ms_ad_sa = MS AD Search Attributes | auths.ms_ad_sa = MS AD Search Attributes | ||||
| auths.smtp_auth = SMTP Authentication Type | auths.smtp_auth = SMTP Authentication Type | ||||
| auths.smtphost = SMTP Host | auths.smtphost = SMTP Host | ||||
| @@ -130,6 +130,7 @@ func parseLDAPConfig(form auth.AuthenticationForm) *models.LDAPConfig { | |||||
| SearchPageSize: pageSize, | SearchPageSize: pageSize, | ||||
| Filter: form.Filter, | Filter: form.Filter, | ||||
| AdminFilter: form.AdminFilter, | AdminFilter: form.AdminFilter, | ||||
| RestrictedFilter: form.RestrictedFilter, | |||||
| AllowDeactivateAll: form.AllowDeactivateAll, | AllowDeactivateAll: form.AllowDeactivateAll, | ||||
| Enabled: true, | Enabled: true, | ||||
| }, | }, | ||||
| @@ -74,6 +74,11 @@ | |||||
| <label for="admin_filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label> | <label for="admin_filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label> | ||||
| <input id="admin_filter" name="admin_filter" value="{{$cfg.AdminFilter}}"> | <input id="admin_filter" name="admin_filter" value="{{$cfg.AdminFilter}}"> | ||||
| </div> | </div> | ||||
| <div class="field"> | |||||
| <label for="restricted_filter">{{.i18n.Tr "admin.auths.restricted_filter"}}</label> | |||||
| <input id="restricted_filter" name="restricted_filter" value="{{$cfg.RestrictedFilter}}"> | |||||
| <p class="help">{{.i18n.Tr "admin.auths.restricted_filter_helper"}}</p> | |||||
| </div> | |||||
| <div class="field"> | <div class="field"> | ||||
| <label for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label> | <label for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label> | ||||
| <input id="attribute_username" name="attribute_username" value="{{$cfg.AttributeUsername}}" placeholder="{{.i18n.Tr "admin.auths.attribute_username_placeholder"}}"> | <input id="attribute_username" name="attribute_username" value="{{$cfg.AttributeUsername}}" placeholder="{{.i18n.Tr "admin.auths.attribute_username_placeholder"}}"> | ||||
| @@ -46,6 +46,11 @@ | |||||
| <label for="admin_filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label> | <label for="admin_filter">{{.i18n.Tr "admin.auths.admin_filter"}}</label> | ||||
| <input id="admin_filter" name="admin_filter" value="{{.admin_filter}}"> | <input id="admin_filter" name="admin_filter" value="{{.admin_filter}}"> | ||||
| </div> | </div> | ||||
| <div class="field"> | |||||
| <label for="restricted_filter">{{.i18n.Tr "admin.auths.restricted_filter"}}</label> | |||||
| <input id="restricted_filter" name="admin_filter" value="{{.restricted_filter}}"> | |||||
| <p class="help">{{.i18n.Tr "admin.auths.restricted_filter_helper"}}</p> | |||||
| </div> | |||||
| <div class="field"> | <div class="field"> | ||||
| <label for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label> | <label for="attribute_username">{{.i18n.Tr "admin.auths.attribute_username"}}</label> | ||||
| <input id="attribute_username" name="attribute_username" value="{{.attribute_username}}" placeholder="{{.i18n.Tr "admin.auths.attribute_username_placeholder"}}"> | <input id="attribute_username" name="attribute_username" value="{{.attribute_username}}" placeholder="{{.i18n.Tr "admin.auths.attribute_username_placeholder"}}"> | ||||