package rfc2307
import (
"errors"
"fmt"
"io/ioutil"
"reflect"
"testing"
"gopkg.in/ldap.v2"
"github.com/openshift/origin/pkg/auth/ldaputil"
"github.com/openshift/origin/pkg/auth/ldaputil/testclient"
"github.com/openshift/origin/pkg/cmd/admin/groups/sync/syncerror"
)
func newTestLDAPInterface(client ldap.Client) *LDAPInterface {
// below are common test implementations of LDAPInterface fields
groupQuery := ldaputil.LDAPQueryOnAttribute{
LDAPQuery: ldaputil.LDAPQuery{
BaseDN: "ou=groups,dc=example,dc=com",
Scope: ldaputil.ScopeWholeSubtree,
DerefAliases: ldaputil.DerefAliasesAlways,
TimeLimit: 0,
Filter: "objectClass=groupOfNames",
},
QueryAttribute: "dn",
}
groupNameAttributes := []string{"cn"}
groupMembershipAttributes := []string{"member"}
userQuery := ldaputil.LDAPQueryOnAttribute{
LDAPQuery: ldaputil.LDAPQuery{
BaseDN: "ou=users,dc=example,dc=com",
Scope: ldaputil.ScopeWholeSubtree,
DerefAliases: ldaputil.DerefAliasesAlways,
TimeLimit: 0,
Filter: "objectClass=inetOrgPerson",
},
QueryAttribute: "dn",
}
userNameAttributes := []string{"cn"}
errorHandler := syncerror.NewCompoundHandler(
syncerror.NewMemberLookupOutOfBoundsSuppressor(ioutil.Discard),
syncerror.NewMemberLookupMemberNotFoundSuppressor(ioutil.Discard),
)
return NewLDAPInterface(testclient.NewConfig(client),
groupQuery,
groupNameAttributes,
groupMembershipAttributes,
userQuery,
userNameAttributes,
errorHandler)
}
// newTestUser returns a new LDAP entry with the CN
func newTestUser(CN string) *ldap.Entry {
return ldap.NewEntry(fmt.Sprintf("cn=%s,ou=users,dc=example,dc=com", CN), map[string][]string{"cn": {CN}})
}
// newTestGroup returns a new LDAP entry with the given CN and member
func newTestGroup(CN, member string) *ldap.Entry {
DN := fmt.Sprintf("cn=%s,ou=groups,dc=example,dc=com", CN)
if len(CN) > 0 {
return ldap.NewEntry(DN, map[string][]string{"cn": {CN}, "member": {member}})
} else {
// no CN
return ldap.NewEntry(DN, map[string][]string{"member": {member}})
}
}
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: syncerror.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: "out of scope member lookup suppressed",
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=other-example,dc=com")},
},
),
"cn=testUser,ou=users,dc=other-example,dc=com",
ldaputil.NewQueryOutOfBoundsError("cn=testUser,ou=users,dc=other-example,dc=com", "cn=testGroup,ou=groups,dc=example,dc=com"),
),
expectedError: nil,
expectedMembers: []*ldap.Entry{},
},
{
name: "no such object member lookup error suppressed",
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=other-example,dc=com")},
},
),
"cn=testUser,ou=users,dc=other-example,dc=com",
ldaputil.NewNoSuchObjectError("cn=testUser,ou=users,dc=other-example,dc=com"),
),
expectedError: nil,
expectedMembers: []*ldap.Entry{},
},
{
name: "member not found member lookup error suppressed",
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=other-example,dc=com")},
},
),
"cn=testUser,ou=users,dc=other-example,dc=com",
ldaputil.NewEntryNotFoundError("cn=testUser,ou=users,dc=other-example,dc=com", "objectClass=groupOfNames"),
),
expectedError: nil,
expectedMembers: []*ldap.Entry{},
},
{
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 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 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 TestUserEntryFor(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=testUser,ou=users,dc=example,dc=com": newTestUser("testUser"),
},
expectedError: nil,
expectedEntry: newTestUser("testUser"),
},
{
name: "search request failure",
queryBaseDNOverride: "dc=foo",
expectedError: ldaputil.NewQueryOutOfBoundsError("cn=testUser,ou=users,dc=example,dc=com", "dc=foo"),
expectedEntry: nil,
},
{
name: "query failure",
client: testclient.NewMatchingSearchErrorClient(
testclient.New(),
"cn=testUser,ou=users,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=testUser,ou=users,dc=example,dc=com": {newTestUser("testUser")},
},
),
expectedError: nil,
expectedEntry: newTestUser("testUser"),
},
}
for _, testCase := range testCases {
ldapInterface := newTestLDAPInterface(testCase.client)
if len(testCase.cacheSeed) > 0 {
ldapInterface.cachedUsers = testCase.cacheSeed
}
if len(testCase.queryBaseDNOverride) > 0 {
ldapInterface.userQuery.BaseDN = testCase.queryBaseDNOverride
}
entry, err := ldapInterface.userEntryFor("cn=testUser,ou=users,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)
}
}
}