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

user_ldap: Filter groups after nested groups #8227

Closed
wants to merge 4 commits into from

Conversation

Cybso
Copy link

@Cybso Cybso commented Feb 7, 2018

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 [email protected]

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 <[email protected]>
@rullzer rullzer requested a review from blizzz February 7, 2018 14:03
@Cybso Cybso changed the title user_ldap: Filter groups after nexted groups user_ldap: Filter groups after nested groups Feb 7, 2018
@Cybso
Copy link
Author

Cybso commented Feb 7, 2018

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 <[email protected]>
@Cybso
Copy link
Author

Cybso commented Feb 7, 2018

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 <[email protected]>
@codecov
Copy link

codecov bot commented Feb 7, 2018

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 Files Coverage Δ Complexity Δ
apps/user_ldap/lib/Group_LDAP.php 62.61% <75%> (-0.32%) 179 <9> (+3)
apps/dav/lib/Files/FileSearchBackend.php 42.61% <0%> (-29.27%) 71% <0%> (+22%)
lib/private/Server.php 83.18% <0%> (-0.1%) 282% <0%> (ø)
settings/templates/settings/admin/tipstricks.php 0% <0%> (ø) 0% <0%> (ø) ⬇️
.../dav/lib/Connector/Sabre/Exception/InvalidPath.php 81.81% <0%> (ø) 3% <0%> (ø) ⬇️
...pps/files_external/lib/Service/StoragesService.php 92.67% <0%> (ø) 59% <0%> (?)
apps/files_external/lib/Lib/Auth/OAuth2/OAuth2.php 0% <0%> (ø) 1% <0%> (?)
...ernal/lib/Lib/Auth/Password/SessionCredentials.php 42.85% <0%> (ø) 4% <0%> (?)
apps/files_external/lib/AppInfo/Application.php 0% <0%> (ø) 4% <0%> (?)
apps/files_external/lib/Lib/Auth/OAuth1/OAuth1.php 0% <0%> (ø) 1% <0%> (?)
... and 92 more

@MorrisJobke
Copy link
Member

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

@blizzz
Copy link
Member

blizzz commented Mar 7, 2018

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
Copy link
Author

Cybso commented Mar 7, 2018

@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
Member

blizzz commented Mar 7, 2018

@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
Copy link
Author

Cybso commented Mar 7, 2018

@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
Member

blizzz commented Mar 7, 2018

@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.

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

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

@Cybso
Copy link
Author

Cybso commented Jan 28, 2019

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

Copy link
Member

@blizzz blizzz left a comment

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
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
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
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
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
Member

blizzz commented Mar 1, 2019

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

Sign up for free to 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