Skip to content

user_ldap: Filter groups after nested groups - #8227

Closed
Cybso wants to merge 4 commits into
nextcloud:masterfrom
Cybso:fix-nested-group-filter
Closed

user_ldap: Filter groups after nested groups#8227
Cybso wants to merge 4 commits into
nextcloud:masterfrom
Cybso:fix-nested-group-filter

Conversation

@Cybso

@CybsoCybso commented Feb 7, 2018

Copy link
Copy Markdown

Currently groupsMatchFilter is called before nested groups are resolved.
This basicly breaks this feature since it is not possible to inherit
membership in a group from another group.

Minimal example:

 Group filter: (&(objectClass=group),(cn=nextcloud))
Nested groups: enabled
cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
objectClass: group
cn=IT,ou=groups,dn=company,dn=local
objectClass: group
memberOf: cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
cn=John Doe,ou=users,dn=company,dn=local
objectClass: person
memberOf: cn=IT,ou=groups,dn=company,dn=local

Since 'cn=IT,ou=groups,dn=company,dn=local' doesn't match the group
filter, John wouldn't be a member of group 'nextcloud'.

This patch fixes this by filtering the groups after all nested groups
have been collected. If nested groups is disabled the result will be the
same as without this patch.

Signed-off-by: Roland Tapken roland@bitarbeiter.net

Currently groupsMatchFilter is called before nested groups are resolved.
This basicly breaks this feature since it is not possible to inherit
membership in a group from another group.
Minimal example:
Group filter: (&(objectClass=group),(cn=nextcloud))
Nested groups: enabled
cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
objectClass: group
cn=IT,ou=groups,dn=company,dn=local
objectClass: group
memberOf: cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
cn=John Doe,ou=users,dn=company,dn=local
objectClass: person
memberOf: cn=IT,ou=groups,dn=company,dn=local
Since 'cn=IT,ou=groups,dn=company,dn=local' doesn't match the group
filter, John wouldn't be a member of group 'nextcloud'.
This patch fixes this by filtering the groups after all nested groups
have been collected. If nested groups is disabled the result will be the
same as without this patch.
Signed-off-by: Roland Tapken <roland@bitarbeiter.net>
@rullzer
rullzer requested a review from blizzzFebruary 7, 2018 14:03
@CybsoCybso changed the title user_ldap: Filter groups after nexted groupsuser_ldap: Filter groups after nested groupsFeb 7, 2018
@Cybso

Cybso commented Feb 7, 2018

Copy link
Copy Markdown
Author

Sorry, this patch is incomplete. It only works for one level on indirection and fails if there is a second level:

 Group filter: (&(objectClass=group),(cn=nextcloud))
Nested groups: enabled
cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
objectClass: group
cn=IT,ou=groups,dn=company,dn=local
objectClass: group
memberOf: cn=nextcloud,ou=Nextcloud,ou=groups,dn=company,dn=local
cn=Administrators,ou=groups,dn=company,dn=local
objectClass: group
memberOf: cn=IT,ou=groups,dn=company,dn=local
cn=John Doe,ou=users,dn=company,dn=local
objectClass: person
memberOf: cn=Administrators,ou=groups,dn=company,dn=local

I'll see if I can rewrite the patch and push it.

The previous patch fixed the problem only for one level of indirection
because groupsMatchFilter() had been applied on each recursive call (and
thus there would be no second level if the first level fails the check).
This new implementation replaces the recursive call with a stack that
iterates all nested groups before filtering with groupsMatchFilter().
Signed-off-by: Roland Tapken <roland@bitarbeiter.net>
@Cybso

Cybso commented Feb 7, 2018

Copy link
Copy Markdown
Author

The updated pull request is a more complex change, but it ensures that nested groups are resolved until their root.

Nested groups are now cached in a CappedMemoryCache object to reduce
queries to the LDAP backend.
Signed-off-by: Roland Tapken <roland@bitarbeiter.net>
@codecov

codecovBot commented Feb 7, 2018

Copy link
Copy Markdown

Codecov Report

Merging #8227 into master will decrease coverage by 1.06%.
The diff coverage is 75%.

@@ Coverage Diff @@## master #8227 +/- ##
============================================
- Coverage 52.75% 51.69% -1.07% - Complexity 24062 25392 +1330 
============================================
Files 1506 1599 +93 Lines 90305 95122 +4817 Branches 1376 1376 ============================================
+ Hits 47643 49175 +1532 - Misses 42662 45947 +3285
Impacted FilesCoverage ΔComplexity Δ
apps/user_ldap/lib/Group_LDAP.php62.61% <75%> (-0.32%)179 <9> (+3)
apps/dav/lib/Files/FileSearchBackend.php42.61% <0%> (-29.27%)71% <0%> (+22%)
lib/private/Server.php83.18% <0%> (-0.1%)282% <0%> (ø)
settings/templates/settings/admin/tipstricks.php0% <0%> (ø)0% <0%> (ø)⬇️
.../dav/lib/Connector/Sabre/Exception/InvalidPath.php81.81% <0%> (ø)3% <0%> (ø)⬇️
...pps/files_external/lib/Service/StoragesService.php92.67% <0%> (ø)59% <0%> (?)
apps/files_external/lib/Lib/Auth/OAuth2/OAuth2.php0% <0%> (ø)1% <0%> (?)
...ernal/lib/Lib/Auth/Password/SessionCredentials.php42.85% <0%> (ø)4% <0%> (?)
apps/files_external/lib/AppInfo/Application.php0% <0%> (ø)4% <0%> (?)
apps/files_external/lib/Lib/Auth/OAuth1/OAuth1.php0% <0%> (ø)1% <0%> (?)
... and 92 more

…anymore.
Signed-off-by: Roland Tapken <roland@bitarbeiter.net>
@MorrisJobke

Copy link
Copy Markdown
Member

@nextcloud/ldap Could you please have a look at this? It's already lying around for too long.

@blizzz

Copy link
Copy Markdown
Member

Currently groupsMatchFilter is called before nested groups are resolved.
This basicly breaks this feature since it is not possible to inherit
membership in a group from another group.

Yes, when the filter is as strict as that. With this changes, the filter does not apply to the subgroups so their members are being read despite that they are not whitelisted.

@Cybso

Cybso commented Mar 7, 2018

Copy link
Copy Markdown
Author

@blizzz Thanks for your feedback. No, the filter is still applied to the subgroups, but only at the end after all sub groups have been resolved. This is the only way to allow the usage of "transparent" distributions groups (like "IT" and "Administrator" in the example) that are common in larger Active Directory configurations.

@blizzz

Copy link
Copy Markdown
Member

@Cybso that's my point. It allows members of those groups to be retrieved while the groups themselves are not white listed.

I am sure it makes sense from one point of view, but rather not from another and constitutes a behavioural change.

Further more, the better approach is perhaps to solely rely on LDAP_MATCHING_RULE_IN_CHAIN so that the LDAP server resolves the the subgroups. I never tested it with OpenLDAP, however it would also be the better performing approach.

@Cybso

Cybso commented Mar 7, 2018

Copy link
Copy Markdown
Author

@blizzz This is not for members of groups but for memberships of a user, and even if the intermediate groups are not listed the user is a member of the groups at the leaves.

LDAP_MATCHING_RULE_IN_CHAIN resolves the members of a group recursively, but in this case the opposite is required (get all groups where a person is member of, direct or indirect) and as far as I know there is no search syntax that provides this feature besides to recursively climb down the tree.

In other words: the user will be listed when querying for the group's member with LDAP_MATCHING_RULE_IN_CHAIN, so _getGroupDNsFromMemberOf($DN) should return the group, too.

@blizzz

Copy link
Copy Markdown
Member

@blizzz This is not for members of groups but for memberships of a user, and even if the intermediate groups are not listed the user is a member of the groups at the leaves.

Ah, excuse me, I was mentally in another place of the code 🙊

LDAP_MATCHING_RULE_IN_CHAIN resolves the members of a group recursively, but in this case the opposite is required (get all groups where a person is member of, direct or indirect) and as far as I know there is no search syntax that provides this feature besides to recursively climb down the tree.

True.

Can I ask also here for unit tests? TestCase offers invokePrivate() for calling restricted methods directly.

@MorrisJobkeMorrisJobke added the stale Ticket or PR with no recent activity label Jun 19, 2018
@MorrisJobkeMorrisJobke added 2. developing Work in progress and removed 3. to review Waiting for reviews labels Jul 9, 2018
@nextcloud-botnextcloud-bot removed the stale Ticket or PR with no recent activity label Jul 9, 2018
@MorrisJobke

Copy link
Copy Markdown
Member

@blizzz Should we take it as it is or wait longer?

@Cybso

Copy link
Copy Markdown
Author

I am very sorry, but at the moment I am extremely busy both professionally and privately, so I did not have the time to do that and probably will not find an opportunity to get into the test framework in the foreseeable future.

See also #8230

@blizzzblizzz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have some nitpicks but it is essentially done. I'd look into it when i am back. It's to the most degree also a bug fix, so should not be affected by the freeze for 16 imho.

$seen[$group] = 1;

// Resolve nested groups
if (isset($cachedNestedGroups[$group])) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this should be $this->cachedNestedGroups[$group]


// Resolve nested groups
if (isset($cachedNestedGroups[$group])) {
$nestedGroups = $cachedNestedGroups[$group];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here as well

if (!is_array($nestedGroups)) {
$nestedGroups = [];
}
$cachedNestedGroups[$group] = $nestedGroups;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and here

}
$cachedNestedGroups[$group] = $nestedGroups;
}
foreach ($nestedGroups as $nestedGroup) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can be simplified with an array_merge if i am not mistaken

}
}
// Get unique group DN's from those we have visited in the loop
$groups = array_keys($seen);

This comment was marked as off-topic.

@blizzz

Copy link
Copy Markdown
Member

I took over, (extended) follow up PR is #14464

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Cybso@MorrisJobke@blizzz@rullzer@nextcloud-bot