From 7df0fbcb2b9fb5ba414310547507155bcd3d6bdd Mon Sep 17 00:00:00 2001 From: sauls8t Date: Thu, 30 Aug 2018 11:37:05 +0100 Subject: [PATCH] Process mulitple groups in LDAP/AD group filter --- domain/auth/ldap/ad_test.go | 84 ++++++++++++++++++--------------- domain/auth/ldap/local_test.go | 67 +++++++++++++------------- domain/auth/ldap/public_test.go | 70 ++++++++++++++------------- model/auth/ldap.go | 8 ++-- 4 files changed, 121 insertions(+), 108 deletions(-) diff --git a/domain/auth/ldap/ad_test.go b/domain/auth/ldap/ad_test.go index f921e687..10109961 100644 --- a/domain/auth/ldap/ad_test.go +++ b/domain/auth/ldap/ad_test.go @@ -21,6 +21,12 @@ import ( ld "gopkg.in/ldap.v2" ) +// Works against AD server in Azure confgiured using: +// +// https://auth0.com/docs/connector/test-dc +// +// Ensure VM network settings open up ports 389 and 636. + func TestADServer_UserList(t *testing.T) { c := lm.LDAPConfig{} c.ServerHost = "40.117.188.17" @@ -110,7 +116,7 @@ func TestADServer_Groups(t *testing.T) { c.BindDN = "CN=ad-admin,CN=Users,DC=mycompany,DC=local" c.BindPassword = "8B5tNRLvbk8K" c.UserFilter = "" - c.GroupFilter = "(cn=Accounting)" + c.GroupFilter = "(|(cn=Accounting)(cn=IT))" address := fmt.Sprintf("%s:%d", c.ServerHost, c.ServerPort) t.Log("Connecting to AD server", address) @@ -173,49 +179,51 @@ func TestADServer_Groups(t *testing.T) { return } - // Get list of group members - rawMembers := sr.Entries[0].GetAttributeValues("member") - fmt.Printf("%s", sr.Entries[0].DN) + // Get list of group members for each group found. + for _, group := range sr.Entries { + rawMembers := group.GetAttributeValues("member") + fmt.Printf("%s", group.DN) - if len(rawMembers) == 0 { - t.Error("Error: group member attribute returned no users") - return - } - - t.Logf("AD group contains %d members", len(rawMembers)) - - for _, entry := range rawMembers { - // get CN element from DN - parts := strings.Split(entry, ",") - if len(parts) == 0 { + if len(rawMembers) == 0 { + t.Log("Error: group member attribute returned no users") continue } - filter := fmt.Sprintf("(%s)", parts[0]) - usr := ld.NewSearchRequest( - c.BaseDN, - ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, - filter, - []string{"dn", "cn", "givenName", "sn", "mail", "sAMAccountName"}, - nil, - ) - ue, err := l.Search(usr) - if err != nil { - t.Error("Error: unable to execute directory search for group member: ", err.Error()) - return - } + t.Logf("AD group contains %d members", len(rawMembers)) - if len(ue.Entries) > 0 { - for _, ur := range ue.Entries { - t.Logf("[%s] %s (%s %s) @ %s\n", - ur.GetAttributeValue("sAMAccountName"), - ur.GetAttributeValue("cn"), - ur.GetAttributeValue("givenName"), - ur.GetAttributeValue("sn"), - ur.GetAttributeValue("mail")) + for _, entry := range rawMembers { + // get CN element from DN + parts := strings.Split(entry, ",") + if len(parts) == 0 { + continue + } + filter := fmt.Sprintf("(%s)", parts[0]) + + usr := ld.NewSearchRequest( + c.BaseDN, + ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, + filter, + []string{"dn", "cn", "givenName", "sn", "mail", "sAMAccountName"}, + nil, + ) + ue, err := l.Search(usr) + if err != nil { + t.Log("Error: unable to execute directory search for group member: ", err.Error()) + continue + } + + if len(ue.Entries) > 0 { + for _, ur := range ue.Entries { + t.Logf("[%s] %s (%s %s) @ %s\n", + ur.GetAttributeValue("sAMAccountName"), + ur.GetAttributeValue("cn"), + ur.GetAttributeValue("givenName"), + ur.GetAttributeValue("sn"), + ur.GetAttributeValue("mail")) + } + } else { + t.Log("group member search failed:", filter) } - } else { - t.Log("group member search failed:", filter) } } } diff --git a/domain/auth/ldap/local_test.go b/domain/auth/ldap/local_test.go index d99a5d65..9c63d106 100644 --- a/domain/auth/ldap/local_test.go +++ b/domain/auth/ldap/local_test.go @@ -117,7 +117,7 @@ func TestLocalLDAPServer_UsersInGroup(t *testing.T) { c.BindDN = "cn=admin,dc=planetexpress,dc=com" c.BindPassword = "GoodNewsEveryone" c.UserFilter = "" - c.GroupFilter = "(&(objectClass=group)(cn=ship_crew))" + c.GroupFilter = "(&(objectClass=group)(|(cn=ship_crew)(cn=admin_staff)))" address := fmt.Sprintf("%s:%d", c.ServerHost, c.ServerPort) @@ -182,43 +182,44 @@ func TestLocalLDAPServer_UsersInGroup(t *testing.T) { return } - // Get list of group members - rawMembers := sr.Entries[0].GetAttributeValues("member") - - if len(rawMembers) == 0 { - t.Error("Error: group member attribute returned no users") - return - } - - t.Logf("LDAP group contains %d members", len(rawMembers)) - - for _, entry := range rawMembers { - // get CN element from DN - parts := strings.Split(entry, ",") - if len(parts) == 0 { + // Get list of group members per group found. + for _, group := range sr.Entries { + rawMembers := group.GetAttributeValues("member") + if len(rawMembers) == 0 { + t.Log("Error: group member attribute returned no users") continue } - filter := fmt.Sprintf("(%s)", parts[0]) - usr := ld.NewSearchRequest( - c.BaseDN, - ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, - filter, - []string{"dn", "cn", "givenName", "sn", "mail", "uid"}, - nil, - ) - ue, err := l.Search(usr) - if err != nil { - t.Error("Error: unable to execute directory search for group member: ", err.Error()) - return - } + t.Logf("LDAP group contains %d members", len(rawMembers)) - if len(ue.Entries) > 0 { - for _, ur := range ue.Entries { - t.Logf("%s", ur.GetAttributeValue("mail")) + for _, entry := range rawMembers { + // get CN element from DN + parts := strings.Split(entry, ",") + if len(parts) == 0 { + continue + } + filter := fmt.Sprintf("(%s)", parts[0]) + + usr := ld.NewSearchRequest( + c.BaseDN, + ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, + filter, + []string{"dn", "cn", "givenName", "sn", "mail", "uid"}, + nil, + ) + ue, err := l.Search(usr) + if err != nil { + t.Log("Error: unable to execute directory search for group member: ", err.Error()) + continue + } + + if len(ue.Entries) > 0 { + for _, ur := range ue.Entries { + t.Logf("%s", ur.GetAttributeValue("mail")) + } + } else { + t.Log("group member search failed:", filter) } - } else { - t.Log("group member search failed:", filter) } } } diff --git a/domain/auth/ldap/public_test.go b/domain/auth/ldap/public_test.go index 13c42d77..26f388f6 100644 --- a/domain/auth/ldap/public_test.go +++ b/domain/auth/ldap/public_test.go @@ -112,7 +112,7 @@ func TestPublicLDAPServer_Groups(t *testing.T) { c.BindDN = "cn=read-only-admin,dc=example,dc=com" c.BindPassword = "password" c.UserFilter = "" - c.GroupFilter = "(ou=Chemists)" + c.GroupFilter = "(|(ou=mathematicians)(ou=chemists))" address := fmt.Sprintf("%s:%d", c.ServerHost, c.ServerPort) t.Log("Connecting to LDAP server", address) @@ -145,7 +145,7 @@ func TestPublicLDAPServer_Groups(t *testing.T) { attrs := []string{} if len(c.GroupFilter) > 0 { filter = c.GroupFilter - attrs = []string{"dn", "cn"} + attrs = []string{"dn", "cn", "uniqueMember"} } else if len(c.UserFilter) > 0 { filter = c.UserFilter attrs = []string{"dn", "cn", "givenName", "sn", "mail", "uid"} @@ -175,44 +175,46 @@ func TestPublicLDAPServer_Groups(t *testing.T) { return } - // Get list of group members - rawMembers := sr.Entries[0].GetAttributeValues("uniqueMember") - fmt.Printf("%s", sr.Entries[0].DN) + // Get list of group members per group found. + for _, group := range sr.Entries { + t.Log("Found group", group.DN) - if len(rawMembers) == 0 { - t.Error("Error: group member attribute returned no users") - return - } - - t.Logf("LDAP group contains %d members", len(rawMembers)) - - for _, entry := range rawMembers { - // get CN element from DN - parts := strings.Split(entry, ",") - if len(parts) == 0 { + rawMembers := group.GetAttributeValues("uniqueMember") + if len(rawMembers) == 0 { + t.Log("Error: group member attribute returned no users") continue } - filter := fmt.Sprintf("(%s)", parts[0]) - usr := ld.NewSearchRequest( - c.BaseDN, - ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, - filter, - []string{"dn", "cn", "givenName", "sn", "mail", "uid"}, - nil, - ) - ue, err := l.Search(usr) - if err != nil { - t.Error("Error: unable to execute directory search for group member: ", err.Error()) - return - } + t.Logf("LDAP group contains %d members", len(rawMembers)) - if len(ue.Entries) > 0 { - for _, ur := range ue.Entries { - t.Logf("%s", ur.GetAttributeValue("mail")) + for _, entry := range rawMembers { + // get CN element from DN + parts := strings.Split(entry, ",") + if len(parts) == 0 { + continue + } + filter := fmt.Sprintf("(%s)", parts[0]) + + usr := ld.NewSearchRequest( + c.BaseDN, + ld.ScopeWholeSubtree, ld.NeverDerefAliases, 0, 0, false, + filter, + []string{"dn", "cn", "givenName", "sn", "mail", "uid"}, + nil, + ) + ue, err := l.Search(usr) + if err != nil { + t.Log("Error: unable to execute directory search for group member: ", err.Error()) + continue + } + + if len(ue.Entries) > 0 { + for _, ur := range ue.Entries { + t.Logf("%s", ur.GetAttributeValue("mail")) + } + } else { + t.Log("group member search failed:", filter) } - } else { - t.Log("group member search failed:", filter) } } } diff --git a/model/auth/ldap.go b/model/auth/ldap.go index 9b5f66ee..f5970eef 100644 --- a/model/auth/ldap.go +++ b/model/auth/ldap.go @@ -11,9 +11,6 @@ package auth -// LDAPConfig that specifies LDAP server connection details and query filters. -// -// // Example for Active Directory -- filter users that belong to SomeGroupName: // (&(objectCategory=Person)(sAMAccountName=*)(memberOf=cn=SomeGroupName,ou=users,dc=example,dc=com)) // @@ -26,6 +23,11 @@ package auth // Example of group filter that returns users belonging to either Developers or Administrators group: // (&(objectCategory=Group)(|(cn=developers)(cn=administrators))) // +// Sources of filter names: +// https://docs.oracle.com/cd/E26217_01/E26214/html/ldap-filters-attrs-users.html +// https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx + +// LDAPConfig that specifies LDAP server connection details and query filters. type LDAPConfig struct { ServerHost string `json:"serverHost"` ServerPort int `json:"serverPort"`