Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed group detection bug for LDAP prune #6323

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions pkg/cmd/experimental/syncgroups/groupdetector/groupdetector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ type GroupBasedDetector struct {
}

func (l *GroupBasedDetector) Exists(ldapGroupUID string) (bool, error) {
_, err := l.groupGetter.GroupEntryFor(ldapGroupUID)
group, err := l.groupGetter.GroupEntryFor(ldapGroupUID)
if ldaputil.IsQueryOutOfBoundsError(err) || ldaputil.IsEntryNotFoundError(err) || ldaputil.IsNoSuchObjectError(err) {
return false, nil
}
if err != nil {
return false, err
}

if group == nil {
return false, nil
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liggitt amazingly it is possible that when doing a specific search for a specific DN in LDAP we can get nothing back and not NoSuchObject

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the LDAP query look like for the DN lookup?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the LDAP query look like for the DN lookup?

@stevekuznetsov answer this to make sure that it looks sane before merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't set the scope to base object only, that makes sense. We should be, however. I'll look into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there may be larger problems. In either case, QueryForUniqueEntry, when it retrieves nothing, it should throw an ErrorEntryNotFound. Which the detector should recognize as meaning the thing it was looking for doesn't exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, never mind all of that. I can't read. The search request is:

&{cn=group1,ou=groups,ou=adextended,dc=example,dc=com 0 0 0 0 false (objectClass=*) [cn dn] []}

which translates to

&SearchRequest{
    BaseDN:       "cn=group1,ou=groups,ou=adextended,dc=example,dc=com",
    Scope:        ScopeBaseObject,
    DerefAliases: NeverDerefAliases,
    SizeLimit:    None,
    TimeLimit:    None,
    TypesOnly:    false,
    Filter:       "(objectClass=*)",
    Attributes:   ["cn", "dn"],
    Controls:     none,
}

I think everything is working as expected now.

}

return true, nil
}

Expand Down Expand Up @@ -76,13 +80,17 @@ type CompoundDetector struct {
}

func (l *CompoundDetector) Exists(ldapGrouUID string) (bool, error) {
conclusion := false
if len(l.locators) == 0 {
return false, nil
}

conclusion := true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. If you don't have any locators, the result should be false, not true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test that makes sure that no locators results in a false to make sure this never slips.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should allow a CompoundDetector that has no constituent detectors. What would that be useful for?

for _, locator := range l.locators {
opinion, err := locator.Exists(ldapGrouUID)
if err != nil {
return false, err
}
conclusion = conclusion || opinion
conclusion = conclusion && opinion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we return false early if one returns false?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we return false early if one returns false?

Doc on the type guarantees an error in one errors. I'm ok with forcing all successful inquiries to be sure about the decision

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly @deads2k and I decided we want all of the locators to run to ensure that no errors are encountered.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly @deads2k and I decided we want all of the locators to run to ensure that no errors are encountered.

You wanted, I had no strong opinion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

}
return conclusion, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@ func TestGroupBasedDetectorExists(t *testing.T) {
expectedExists: false,
},
{
name: "no error",
name: "no error, no entries",
groupGetter: &puppetGetterExtractor{},
expectedErr: nil,
expectedExists: false,
},
{
name: "no error, has entries",
groupGetter: &puppetGetterExtractor{returnVal: []*ldap.Entry{dummyEntry}},
expectedErr: nil,
expectedExists: true,
},
}
Expand Down Expand Up @@ -126,10 +132,16 @@ func TestCompoundDetectorExists(t *testing.T) {
expectedErr error
expectedExists bool
}{
{
name: "no detectors",
locators: []interfaces.LDAPGroupDetector{},
expectedErr: nil,
expectedExists: false,
},
{
name: "none fail to locate",
locators: []interfaces.LDAPGroupDetector{
NewGroupBasedDetector(&puppetGetterExtractor{}),
NewGroupBasedDetector(&puppetGetterExtractor{returnVal: []*ldap.Entry{dummyEntry}}),
NewMemberBasedDetector(&puppetGetterExtractor{returnVal: []*ldap.Entry{dummyEntry}}),
},
expectedErr: nil,
Expand All @@ -142,17 +154,26 @@ func TestCompoundDetectorExists(t *testing.T) {
NewMemberBasedDetector(&puppetGetterExtractor{returnVal: []*ldap.Entry{dummyEntry}}),
},
expectedErr: nil,
expectedExists: true,
expectedExists: false,
},
{
name: "all fail to locate",
name: "all fail to locate because of errors",
locators: []interfaces.LDAPGroupDetector{
NewGroupBasedDetector(&errEntryNotFoundGetterExtractor{}),
NewMemberBasedDetector(&errOutOfBoundsGetterExtractor{}),
},
expectedErr: nil,
expectedExists: false,
},
{
name: "all locate no entries",
locators: []interfaces.LDAPGroupDetector{
NewGroupBasedDetector(&puppetGetterExtractor{}),
NewMemberBasedDetector(&puppetGetterExtractor{}),
},
expectedErr: nil,
expectedExists: false,
},
{
name: "one errors",
locators: []interfaces.LDAPGroupDetector{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: v1
kind: Group
metadata:
annotations:
openshift.io/ldap.uid: cn=group2,ou=groups,ou=adextended,dc=example,dc=com
openshift.io/ldap.url: LDAP_SERVICE_IP:389
creationTimestamp: null
labels:
openshift.io/ldap.host: LDAP_SERVICE_IP
name: extended-group2
users:
- person1smith@example.com
- person2smith@example.com
- person3smith@example.com
apiVersion: v1
kind: Group
metadata:
annotations:
openshift.io/ldap.uid: cn=group3,ou=groups,ou=adextended,dc=example,dc=com
openshift.io/ldap.url: LDAP_SERVICE_IP:389
creationTimestamp: null
labels:
openshift.io/ldap.host: LDAP_SERVICE_IP
name: extended-group3
users:
- person1smith@example.com
- person5smith@example.com
12 changes: 10 additions & 2 deletions test/extended/ldap_groups.sh
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,11 @@ for (( i=0; i<${#schema[@]}; i++ )); do
openshift ex sync-groups --type=openshift --sync-config=sync-config.yaml --confirm
compare_and_cleanup valid_all_ldap_sync_user_defined.txt



echo -e "\tTEST: Sync all LDAP groups from LDAP server using DN as attribute whenever possible"
openshift ex sync-groups --sync-config=sync-config-dn-everywhere.yaml --confirm
compare_and_cleanup valid_all_ldap_sync_dn_everywhere.txt


# PRUNING
echo -e "\tTEST: Sync all LDAP groups from LDAP server, change LDAP UID, then prune OpenShift groups"
openshift ex sync-groups --sync-config=sync-config.yaml --confirm
Expand All @@ -231,3 +230,12 @@ for (( i=0; i<${#schema[@]}; i++ )); do

popd > /dev/null
done

# special test for ad-extended
pushd ${BASETMPDIR}/augmented-ad > /dev/null
echo -e "\tTEST: Sync all LDAP groups from LDAP server, remove LDAP group metadata entry, then prune OpenShift groups"
openshift ex sync-groups --sync-config=sync-config.yaml --confirm
ldapdelete -x -h $LDAP_SERVICE_IP -p 389 -D cn=Manager,dc=example,dc=com -w admin "${group1_ldapuid}"
openshift ex prune-groups --sync-config=sync-config.yaml --confirm
compare_and_cleanup valid_all_ldap_sync_delete_prune.txt
popd > /dev/null