Skip to content
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
7 changes: 7 additions & 0 deletions apps/user_ldap/lib/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class Configuration {
const AVATAR_PREFIX_NONE = 'none';
const AVATAR_PREFIX_DATA_ATTRIBUTE = 'data:';

public const LDAP_SERVER_FEATURE_UNKNOWN = 'unknown';
public const LDAP_SERVER_FEATURE_AVAILABLE = 'available';
public const LDAP_SERVER_FEATURE_UNAVAILABLE = 'unavailable';

protected $configPrefix = null;
protected $configRead = false;
/**
Expand Down Expand Up @@ -109,6 +113,7 @@ class Configuration {
'ldapDynamicGroupMemberURL' => null,
'ldapDefaultPPolicyDN' => null,
'ldapExtStorageHomeAttribute' => null,
'ldapMatchingRuleInChainState' => self::LDAP_SERVER_FEATURE_UNKNOWN,
);

/**
Expand Down Expand Up @@ -481,6 +486,7 @@ public function getDefaults() {
'ldap_default_ppolicy_dn' => '',
'ldap_user_avatar_rule' => 'default',
'ldap_ext_storage_home_attribute' => '',
'ldap_matching_rule_in_chain_state' => self::LDAP_SERVER_FEATURE_UNKNOWN,
);
}

Expand Down Expand Up @@ -542,6 +548,7 @@ public function getConfigTranslationArray() {
'ldap_dynamic_group_member_url' => 'ldapDynamicGroupMemberURL',
'ldap_default_ppolicy_dn' => 'ldapDefaultPPolicyDN',
'ldap_ext_storage_home_attribute' => 'ldapExtStorageHomeAttribute',
'ldap_matching_rule_in_chain_state' => 'ldapMatchingRuleInChainState',
'ldapIgnoreNamingRules' => 'ldapIgnoreNamingRules', // sysconfig
);
return $array;
Expand Down
1 change: 1 addition & 0 deletions apps/user_ldap/lib/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
* @property string[] ldapBaseGroups
* @property string ldapGroupFilter
* @property string ldapGroupDisplayName
* @property string ldapMatchingRuleInChainState
*/
class Connection extends LDAPUtility {
private $ldapConnectionRes = null;
Expand Down
38 changes: 38 additions & 0 deletions apps/user_ldap/lib/Group_LDAP.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,37 @@ private function _groupMembers($dnGroup, &$seen = null) {
if($groupMembers !== null) {
return $groupMembers;
}

if ($this->access->connection->ldapNestedGroups
&& $this->access->connection->useMemberOfToDetectMembership
&& $this->access->connection->hasMemberOfFilterSupport
&& $this->access->connection->ldapMatchingRuleInChainState !== Configuration::LDAP_SERVER_FEATURE_UNAVAILABLE
) {
$attemptedLdapMatchingRuleInChain = true;
// compatibility hack with servers supporting :1.2.840.113556.1.4.1941:, and others)
$filter = $this->access->combineFilterWithAnd([
$this->access->connection->ldapUserFilter,
$this->access->connection->ldapUserDisplayName . '=*',
'memberof:1.2.840.113556.1.4.1941:=' . $dnGroup
]);
$memberRecords = $this->access->fetchListOfUsers(
$filter,
$this->access->userManager->getAttributes(true)
);
$result = array_reduce($memberRecords, function ($carry, $record) {
$carry[] = $record['dn'][0];
return $carry;
}, []);
if ($this->access->connection->ldapMatchingRuleInChainState === Configuration::LDAP_SERVER_FEATURE_AVAILABLE) {
return $result;
} elseif (!empty($memberRecords)) {
$this->access->connection->ldapMatchingRuleInChainState = Configuration::LDAP_SERVER_FEATURE_AVAILABLE;
$this->access->connection->saveConfiguration();
return $result;
}
// when feature availability is unknown, and the result is empty, continue and test with original approach
}

$seen[$dnGroup] = 1;
$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr);
if (is_array($members)) {
Expand All @@ -244,6 +275,13 @@ private function _groupMembers($dnGroup, &$seen = null) {
$allMembers += $this->getDynamicGroupMembers($dnGroup);

$this->access->connection->writeToCache($cacheKey, $allMembers);
if (isset($attemptedLdapMatchingRuleInChain)
&& $this->access->connection->ldapMatchingRuleInChainState === Configuration::LDAP_SERVER_FEATURE_UNKNOWN
&& !empty($allMembers)
) {
$this->access->connection->ldapMatchingRuleInChainState = Configuration::LDAP_SERVER_FEATURE_UNAVAILABLE;
$this->access->connection->saveConfiguration();
}
return $allMembers;
}

Expand Down
60 changes: 49 additions & 11 deletions apps/user_ldap/tests/Group_LDAPTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ private function getAccessMock() {
->method('getConnection')
->will($this->returnValue($connector));

$access->userManager = $this->createMock(Manager::class);

return $access;
}

private function getPluginManagerMock() {
return $this->getMockBuilder('\OCA\User_LDAP\GroupPluginManager')->getMock();
}

/**
* @param Access|\PHPUnit_Framework_MockObject_MockObject $access
*/
Expand Down Expand Up @@ -119,12 +121,21 @@ public function testCountEmptySearchString() {
}
return [];
});

$access->expects($this->any())
->method('combineFilterWithAnd')
->willReturn('pseudo=filter');
$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnValue([])); // return val does not matter here
// for primary groups
$access->expects($this->once())
->method('countUsers')
->will($this->returnValue(2));

$access->userManager->expects($this->any())
->method('getAttributes')
->willReturn(['displayName', 'mail']);

$groupBackend = new GroupLDAP($access, $pluginManager);
$users = $groupBackend->countUsersInGroup('group');

Expand Down Expand Up @@ -164,6 +175,13 @@ public function testCountWithSearchString() {
->will($this->returnCallback(function() {
return 'foobar' . \OC::$server->getSecureRandom()->generate(7);
}));
$access->expects($this->any())
->method('combineFilterWithAnd')
->willReturn('pseudo=filter');

$access->userManager->expects($this->any())
->method('getAttributes')
->willReturn(['displayName', 'mail']);

$groupBackend = new GroupLDAP($access,$pluginManager);
$users = $groupBackend->countUsersInGroup('group', '3');
Expand Down Expand Up @@ -193,7 +211,7 @@ public function testCountUsersWithPlugin() {
$ldap = new GroupLDAP($access, $pluginManager);

$this->assertEquals($ldap->countUsersInGroup('gid', 'search'),42);
}
}

public function testGidNumber2NameSuccess() {
$access = $this->getAccessMock();
Expand Down Expand Up @@ -533,7 +551,13 @@ public function testUsersInGroupPrimaryMembersOnly() {
$access->expects($this->exactly(2))
->method('nextcloudUserNames')
->willReturnOnConsecutiveCalls(['lisa', 'bart', 'kira', 'brad'], ['walle', 'dino', 'xenia']);
$access->userManager = $this->createMock(Manager::class);
$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnValue([])); // return val does not matter here

$access->userManager->expects($this->any())
->method('getAttributes')
->willReturn(['displayName', 'mail']);

$groupBackend = new GroupLDAP($access, $pluginManager);
$users = $groupBackend->usersInGroup('foobar');
Expand Down Expand Up @@ -568,7 +592,12 @@ public function testUsersInGroupPrimaryAndUnixMembers() {
$access->expects($this->once())
->method('nextcloudUserNames')
->will($this->returnValue(array('lisa', 'bart', 'kira', 'brad')));
$access->userManager = $this->createMock(Manager::class);
$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnValue([])); // return val does not matter here
$access->userManager->expects($this->any())
->method('getAttributes')
->willReturn(['displayName', 'mail']);

$groupBackend = new GroupLDAP($access, $pluginManager);
$users = $groupBackend->usersInGroup('foobar');
Expand Down Expand Up @@ -602,10 +631,19 @@ public function testCountUsersInGroupPrimaryMembersOnly() {
$access->expects($this->any())
->method('groupname2dn')
->will($this->returnValue('cn=foobar,dc=foo,dc=bar'));

$access->expects($this->any())
->method('fetchListOfUsers')
->will($this->returnValue([])); // return val does not matter here
$access->expects($this->once())
->method('countUsers')
->will($this->returnValue(4));
$access->expects($this->any())
->method('combineFilterWithAnd')
->willReturn('pseudo=filter');

$access->userManager->expects($this->any())
->method('getAttributes')
->willReturn(['displayName', 'mail']);

$groupBackend = new GroupLDAP($access, $pluginManager);
$users = $groupBackend->countUsersInGroup('foobar');
Expand Down Expand Up @@ -770,7 +808,7 @@ public function testCreateGroupWithPlugin() {
$this->assertEquals($ldap->createGroup('gid'),true);
}


public function testCreateGroupFailing() {
$this->expectException(\Exception::class);

Expand Down Expand Up @@ -825,7 +863,7 @@ public function testDeleteGroupWithPlugin() {
$this->assertEquals($ldap->deleteGroup('gid'),'result');
}


public function testDeleteGroupFailing() {
$this->expectException(\Exception::class);

Expand Down Expand Up @@ -871,7 +909,7 @@ public function testAddToGroupWithPlugin() {
$this->assertEquals($ldap->addToGroup('uid', 'gid'),'result');
}


public function testAddToGroupFailing() {
$this->expectException(\Exception::class);

Expand Down Expand Up @@ -917,7 +955,7 @@ public function testRemoveFromGroupWithPlugin() {
$this->assertEquals($ldap->removeFromGroup('uid', 'gid'),'result');
}


public function testRemoveFromGroupFailing() {
$this->expectException(\Exception::class);

Expand Down Expand Up @@ -963,7 +1001,7 @@ public function testGetGroupDetailsWithPlugin() {
$this->assertEquals($ldap->getGroupDetails('gid'),'result');
}


public function testGetGroupDetailsFailing() {
$this->expectException(\Exception::class);

Expand Down