func TestGroupEntryFor(t *testing.T) { var testCases = []struct { name string cacheSeed map[string]*ldap.Entry queryBaseDNOverride string client ldap.Client expectedError error expectedEntry *ldap.Entry }{ { name: "cached get", cacheSeed: map[string]*ldap.Entry{"cn=testGroup,ou=groups,dc=example,dc=com": newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com")}, expectedError: nil, expectedEntry: newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com"), }, { name: "search request failure", queryBaseDNOverride: "dc=foo", expectedError: ldaputil.NewQueryOutOfBoundsError("cn=testGroup,ou=groups,dc=example,dc=com", "dc=foo"), expectedEntry: nil, }, { name: "query failure", client: testclient.NewMatchingSearchErrorClient( testclient.New(), "cn=testGroup,ou=groups,dc=example,dc=com", errors.New("generic search error"), ), expectedError: errors.New("generic search error"), expectedEntry: nil, }, { name: "no errors", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "cn=testGroup,ou=groups,dc=example,dc=com": {newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com")}, }, ), expectedError: nil, expectedEntry: newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com"), }, } for _, testCase := range testCases { ldapInterface := newTestLDAPInterface(testCase.client) if len(testCase.cacheSeed) > 0 { ldapInterface.cachedGroups = testCase.cacheSeed } if len(testCase.queryBaseDNOverride) > 0 { ldapInterface.groupQuery.BaseDN = testCase.queryBaseDNOverride } entry, err := ldapInterface.GroupEntryFor("cn=testGroup,ou=groups,dc=example,dc=com") if !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedError, err) } if !reflect.DeepEqual(entry, testCase.expectedEntry) { t.Errorf("%s: incorrect entry returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedEntry, entry) } } }
func TestExtractMembers(t *testing.T) { var testCases = []struct { name string client ldap.Client expectedError error expectedMembers []*ldap.Entry }{ { name: "group lookup errors", client: testclient.NewMatchingSearchErrorClient( testclient.New(), "cn=testGroup,ou=groups,dc=example,dc=com", errors.New("generic search error"), ), expectedError: errors.New("generic search error"), expectedMembers: nil, }, { name: "member lookup errors", // this is a nested test client, the first nest tries to error on the user DN // the second nest attempts to give back from the DN mapping // the third nest is the default "safe" impl from ldaputil client: testclient.NewMatchingSearchErrorClient( testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "cn=testGroup,ou=groups,dc=example,dc=com": {newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com")}, }, ), "cn=testUser,ou=users,dc=example,dc=com", errors.New("generic search error"), ), expectedError: interfaces.NewMemberLookupError("cn=testGroup,ou=groups,dc=example,dc=com", "cn=testUser,ou=users,dc=example,dc=com", errors.New("generic search error")), expectedMembers: nil, }, { name: "no errors", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "cn=testGroup,ou=groups,dc=example,dc=com": {newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com")}, "cn=testUser,ou=users,dc=example,dc=com": {newTestUser("testUser")}, }, ), expectedError: nil, expectedMembers: []*ldap.Entry{newTestUser("testUser")}, }, } for _, testCase := range testCases { ldapInterface := newTestLDAPInterface(testCase.client) members, err := ldapInterface.ExtractMembers("cn=testGroup,ou=groups,dc=example,dc=com") if !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedError, err) } if !reflect.DeepEqual(members, testCase.expectedMembers) { t.Errorf("%s: incorrect members returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedMembers, members) } } }
func TestPopulateCache(t *testing.T) { var testCases = []struct { name string cacheSeed map[string][]*ldap.Entry searchDNOverride string client ldap.Client expectedError error expectedCache map[string][]*ldap.Entry }{ { name: "cache already populated", cacheSeed: map[string][]*ldap.Entry{ "testGroup": {newTestUser("testUser", "testGroup")}, }, expectedError: nil, expectedCache: map[string][]*ldap.Entry{ "testGroup": {newTestUser("testUser", "testGroup")}, }, }, { name: "user query error", client: testclient.NewMatchingSearchErrorClient( testclient.New(), "ou=users,dc=example,dc=com", errors.New("generic search error"), ), expectedError: errors.New("generic search error"), expectedCache: make(map[string][]*ldap.Entry), // won't be nil but will be empty }, { name: "cache populated correctly", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=users,dc=example,dc=com": {newTestUser("testUser", "testGroup")}, }, ), expectedError: nil, expectedCache: map[string][]*ldap.Entry{ "testGroup": {newTestUser("testUser", "testGroup")}, }, }, } for _, testCase := range testCases { ldapInterface := newTestADLDAPInterface(testCase.client) if len(testCase.cacheSeed) > 0 { ldapInterface.ldapGroupToLDAPMembers = testCase.cacheSeed ldapInterface.cacheFullyPopulated = true } err := ldapInterface.populateCache() if !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedError, err) } if !reflect.DeepEqual(testCase.expectedCache, ldapInterface.ldapGroupToLDAPMembers) { t.Errorf("%s: incorrect cache state:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedCache, ldapInterface.ldapGroupToLDAPMembers) } } }
func TestListGroups(t *testing.T) { var testCases = []struct { name string client ldap.Client groupUIDAttribute string expectedError error expectedGroups []string }{ { name: "query errors", client: testclient.NewMatchingSearchErrorClient( testclient.New(), "ou=groups,dc=example,dc=com", errors.New("generic search error"), ), expectedError: errors.New("generic search error"), expectedGroups: nil, }, { name: "no UID on entry", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=groups,dc=example,dc=com": {newTestGroup("", "cn=testUser,ou=users,dc=example,dc=com")}, }, ), groupUIDAttribute: "cn", expectedError: fmt.Errorf("unable to find LDAP group UID for %s", newTestGroup("", "cn=testUser,ou=users,dc=example,dc=com")), expectedGroups: nil, }, { name: "no error", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=groups,dc=example,dc=com": {newTestGroup("testGroup", "cn=testUser,ou=users,dc=example,dc=com")}, }, ), expectedError: nil, expectedGroups: []string{"cn=testGroup,ou=groups,dc=example,dc=com"}, }, } for _, testCase := range testCases { ldapInterface := newTestLDAPInterface(testCase.client) if len(testCase.groupUIDAttribute) > 0 { ldapInterface.groupQuery.QueryAttribute = testCase.groupUIDAttribute } groupNames, err := ldapInterface.ListGroups() if !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedError, err) } if !reflect.DeepEqual(groupNames, testCase.expectedGroups) { t.Errorf("%s: incorrect entry returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedGroups, groupNames) } } }
func TestExtractMembers(t *testing.T) { // we don't have a test case for an error on a bad search request as search request errors can only occur if // the search attribute is the DN, and we do not allow DN to be a group UID for this schema var testCases = []struct { name string cacheSeed map[string][]*ldap.Entry client ldap.Client expectedError error expectedMembers []*ldap.Entry }{ { name: "members cached", cacheSeed: map[string][]*ldap.Entry{ "testGroup": {newTestUser("testUser", "testGroup")}, }, expectedError: nil, expectedMembers: []*ldap.Entry{newTestUser("testUser", "testGroup")}, }, { name: "user query error", client: testclient.NewMatchingSearchErrorClient( testclient.New(), "ou=users,dc=example,dc=com", errors.New("generic search error"), ), expectedError: errors.New("generic search error"), expectedMembers: nil, }, { name: "no errors", client: testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=users,dc=example,dc=com": {newTestUser("testUser", "testGroup")}, }, ), expectedError: nil, expectedMembers: []*ldap.Entry{newTestUser("testUser", "testGroup")}, }, } for _, testCase := range testCases { ldapInterface := newTestADLDAPInterface(testCase.client) if len(testCase.cacheSeed) > 0 { ldapInterface.ldapGroupToLDAPMembers = testCase.cacheSeed } members, err := ldapInterface.ExtractMembers("testGroup") if !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedError, err) } if !reflect.DeepEqual(members, testCase.expectedMembers) { t.Errorf("%s: incorrect members returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", testCase.name, testCase.expectedMembers, members) } } }
func TestQueryWithPaging(t *testing.T) { expectedResult := &ldap.SearchResult{ Entries: []*ldap.Entry{ldap.NewEntry("cn=paging,ou=paging,dc=paging,dc=com", map[string][]string{"paging": {"true"}})}, } testConfig := testclient.NewConfig(testclient.NewPagingOnlyClient(testclient.New(), expectedResult, )) testSearchRequest := &ldap.SearchRequest{ BaseDN: "dc=example,dc=com", Scope: ldap.ScopeWholeSubtree, DerefAliases: int(DefaultDerefAliases), SizeLimit: DefaultSizeLimit, TimeLimit: DefaultTimeLimit, TypesOnly: DefaultTypesOnly, Filter: "(objectClass=*)", Attributes: append(DefaultAttributes), Controls: []ldap.Control{ldap.NewControlPaging(5)}, } // test that a search request with paging controls gets correctly routed to the SearchWithPaging call response, err := QueryForEntries(testConfig, testSearchRequest) if err != nil { t.Errorf("query with paging control should not create error, but got %v", err) } if !reflect.DeepEqual(expectedResult.Entries, response) { t.Errorf("query with paging did not return correct response: expected %v, got %v", expectedResult.Entries, response) } }
// TestErrEntryNotFound checks that we wrap a zero-length list of results correctly if we search for a unique entry func TestErrEntryNotFound(t *testing.T) { testConfig := testclient.NewConfig(testclient.New()) testSearchRequest := &ldap.SearchRequest{ BaseDN: "dc=example,dc=com", Scope: ldap.ScopeWholeSubtree, DerefAliases: int(DefaultDerefAliases), SizeLimit: DefaultSizeLimit, TimeLimit: DefaultTimeLimit, TypesOnly: DefaultTypesOnly, Filter: "(objectClass=*)", Attributes: append(DefaultAttributes), Controls: DefaultControls, } expectedErr := &errEntryNotFound{baseDN: "dc=example,dc=com", filter: "(objectClass=*)"} // test that a unique search errors on no result if _, err := QueryForUniqueEntry(testConfig, testSearchRequest); !reflect.DeepEqual(err, expectedErr) { t.Errorf("query for unique entry did not get correct error:\n\texpected:\n\t%v\n\tgot:\n\t%v", expectedErr, err) } // test that a non-unique search doesn't error if _, err := QueryForEntries(testConfig, testSearchRequest); !reflect.DeepEqual(err, nil) { t.Errorf("query for entries did not get correct error:\n\texpected:\n\t%v\n\tgot:\n\t%v", nil, err) } }
// TestErrNoSuchObject tests that our LDAP search correctly wraps the LDAP server error func TestErrNoSuchObject(t *testing.T) { var testCases = []struct { name string searchRequest *ldap.SearchRequest expectedError error }{ { name: "valid search", searchRequest: &ldap.SearchRequest{ BaseDN: "uid=john,o=users,dc=example,dc=com", }, expectedError: nil, }, { name: "invalid search", searchRequest: &ldap.SearchRequest{ BaseDN: "ou=groups,dc=example,dc=com", }, expectedError: &errNoSuchObject{baseDN: "ou=groups,dc=example,dc=com"}, }, } for _, testCase := range testCases { testClient := testclient.NewMatchingSearchErrorClient(testclient.New(), "ou=groups,dc=example,dc=com", ldap.NewError(ldap.LDAPResultNoSuchObject, errors.New("")), ) testConfig := testclient.NewConfig(testClient) if _, err := QueryForEntries(testConfig, testCase.searchRequest); !reflect.DeepEqual(err, testCase.expectedError) { t.Errorf("%s: error did not match:\n\texpected:\n\t%v\n\tgot:\n\t%v", testCase.name, testCase.expectedError, err) } } }
// TestPopulateCacheAfterExtractMembers ensures that the cache is only listed as fully populated after a // populateCache call and not after a partial fill from an ExtractMembers call func TestPopulateCacheAfterExtractMembers(t *testing.T) { client := testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=users,dc=example,dc=com": {newTestUser("testUser", "testGroup")}, }, ) ldapInterface := newTestADLDAPInterface(client) _, err := ldapInterface.ExtractMembers("testGroup") if err != nil { t.Errorf("unexpected error: %v", err) } // both queries use the same BaseDN so we change what the client returns to simulate not applying the group-specific filter client.(*testclient.DNMappingClient).DNMapping["ou=users,dc=example,dc=com"] = []*ldap.Entry{ newTestUser("testUser", "testGroup"), newTestUser("testUser2", "testGroup2")} expectedCache := map[string][]*ldap.Entry{ "testGroup": {newTestUser("testUser", "testGroup")}, "testGroup2": {newTestUser("testUser2", "testGroup2")}, } err = ldapInterface.populateCache() if err != nil { t.Errorf("unexpected error: %v", err) } if !reflect.DeepEqual(expectedCache, ldapInterface.ldapGroupToLDAPMembers) { t.Errorf("incorrect cache state:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", expectedCache, ldapInterface.ldapGroupToLDAPMembers) } }
func TestListGroups(t *testing.T) { client := testclient.NewDNMappingClient( testclient.New(), map[string][]*ldap.Entry{ "ou=users,dc=example,dc=com": {newTestUser("testUser", "testGroup")}, }, ) ldapInterface := newTestADLDAPInterface(client) groups, err := ldapInterface.ListGroups() if !reflect.DeepEqual(err, nil) { t.Errorf("listing groups: incorrect error returned:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", nil, err) } if !reflect.DeepEqual(groups, []string{"testGroup"}) { t.Errorf("listing groups: incorrect group list:\n\texpected:\n\t%v\n\tgot:\n\t%v\n", []string{"testGroup"}, groups) } }