diff --git a/group_ldap.php b/group_ldap.php index 0395a4a80..a7a90c758 100644 --- a/group_ldap.php +++ b/group_ldap.php @@ -378,9 +378,11 @@ public function getUserGroups($uid) { && intval($this->access->connection->useMemberOfToDetectMembership) === 1 ) { $groupDNs = $this->access->readAttribute($userDN, 'memberOf'); + if (is_array($groupDNs)) { + $groupDNs = $this->access->groupsMatchFilter($groupDNs); foreach ($groupDNs as $dn) { - $groups[] = $this->access->dn2groupname($dn);; + $groups[] = $this->access->dn2groupname($dn); } } if($primaryGroup !== false) { diff --git a/lib/access.php b/lib/access.php index f38d11d4b..b201220d7 100644 --- a/lib/access.php +++ b/lib/access.php @@ -346,6 +346,33 @@ public function dn2groupname($fdn, $ldapName = null) { return $this->dn2ocname($fdn, $ldapName, false); } + /** + * accepts an array of group DNs and tests whether they match the user + * filter by doing read operations against the group entries. Returns an + * array of DNs that match the filter. + * + * @param string[] $groupDNs + * @return string[] + */ + public function groupsMatchFilter($groupDNs) { + $validGroupDNs = []; + foreach($groupDNs as $dn) { + $cacheKey = 'groupsMatchFilter-'.$dn; + if($this->connection->isCached($cacheKey)) { + if($this->connection->getFromCache($cacheKey)) { + $validGroupDNs[] = $dn; + } + continue; + } + + $result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter); + if(is_array($result)) { + $validGroupDNs[] = $dn; + } + } + return $validGroupDNs; + } + /** * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure * @param string $dn the dn of the user object diff --git a/tests/.htaccess b/tests/.htaccess new file mode 100755 index 000000000..5e068d286 --- /dev/null +++ b/tests/.htaccess @@ -0,0 +1,14 @@ +# Generated by ownCloud on 2015-06-18 14:16:40 +# line below if for Apache 2.4 + +Require all denied + + +# line below if for Apache 2.2 + +deny from all +Satisfy All + + +# section for Apache 2.2 and 2.4 +IndexIgnore * diff --git a/tests/group_ldap.php b/tests/group_ldap.php index aeb306174..f716618ce 100644 --- a/tests/group_ldap.php +++ b/tests/group_ldap.php @@ -404,6 +404,10 @@ public function testGetUserGroupsMemberOf() { ->method('dn2groupname') ->will($this->returnArgument(0)); + $access->expects($this->once()) + ->method('groupsMatchFilter') + ->will($this->returnArgument(0)); + $groupBackend = new GroupLDAP($access); $groups = $groupBackend->getUserGroups('userX'); diff --git a/tests/integration/lib/IntegrationTestAccessGroupsMatchFilter.php b/tests/integration/lib/IntegrationTestAccessGroupsMatchFilter.php new file mode 100644 index 000000000..6560153bb --- /dev/null +++ b/tests/integration/lib/IntegrationTestAccessGroupsMatchFilter.php @@ -0,0 +1,158 @@ +base = $base; + $this->server = [ + 'host' => $host, + 'port' => $port, + 'dn' => $bind, + 'pwd' => $pwd + ]; + } + + /** + * prepares the LDAP environement and sets up a test configuration for + * the LDAP backend. + */ + public function init() { + require('setup-scripts/createExplicitUsers.php'); + require('setup-scripts/createExplicitGroups.php'); + + $this->initLDAPWrapper(); + $this->initConnection(); + $this->initAccess(); + } + + /** + * runs the test cases while outputting progress and result information + * + * If a test failed, the script is exited with return code 1. + */ + public function run() { + $cases = ['case1', 'case2']; + + foreach ($cases as $case) { + print("running $case " . PHP_EOL); + if (!$this->$case()) { + print(PHP_EOL . '>>> !!! Test ' . $case . ' FAILED !!! <<<' . PHP_EOL . PHP_EOL); + exit(1); + } + } + + print('Tests succeeded' . PHP_EOL); + } + + /** + * tests whether the group filter works with one specific group, while the + * input is the same. + * + * @return bool + */ + private function case1() { + $this->connection->setConfiguration(['ldapGroupFilter' => 'cn=RedGroup']); + + $dns = ['cn=RedGroup,ou=Groups,' . $this->base]; + $result = $this->access->groupsMatchFilter($dns); + return ($dns === $result); + } + + /** + * Tests whether a filter for limited groups is effective when more existing + * groups were passed for validation. + * + * @return bool + */ + private function case2() { + $this->connection->setConfiguration(['ldapGroupFilter' => '(|(cn=RedGroup)(cn=PurpleGroup))']); + + $dns = [ + 'cn=RedGroup,ou=Groups,' . $this->base, + 'cn=BlueGroup,ou=Groups,' . $this->base, + 'cn=PurpleGroup,ou=Groups,' . $this->base + ]; + $result = $this->access->groupsMatchFilter($dns); + + $status = + count($result) === 2 + && in_array('cn=RedGroup,ou=Groups,' . $this->base, $result) + && in_array('cn=PurpleGroup,ou=Groups,' . $this->base, $result); + + return $status; + } + + /** + * initializes the Access test instance + */ + private function initAccess() { + $this->access = new \OCA\user_ldap\lib\Access($this->connection, $this->ldap, new FakeManager()); + } + + /** + * initializes the test LDAP wrapper + */ + private function initLDAPWrapper() { + $this->ldap = new LDAP(); + } + + /** + * sets up the LDAP configuration to be used for the test + */ + private function initConnection() { + $this->connection = new \OCA\user_ldap\lib\Connection($this->ldap, '', null); + $this->connection->setConfiguration([ + 'ldapHost' => $this->server['host'], + 'ldapPort' => $this->server['port'], + 'ldapBase' => $this->base, + 'ldapAgentName' => $this->server['dn'], + 'ldapAgentPassword' => $this->server['pwd'], + 'ldapUserFilter' => 'objectclass=inetOrgPerson', + 'ldapUserDisplayName' => 'displayName', + 'ldapGroupDisplayName' => 'cn', + 'ldapLoginFilter' => 'uid=%uid', + 'ldapCacheTTL' => 0, + 'ldapConfigurationActive' => 1, + ]); + } +} + +/** + * Class FakeManager + * + * this is a mock of \OCA\user_ldap\lib\user\Manager which is a dependency of + * Access, that pulls plenty more things in. Because it is not needed in the + * scope of these tests, we replace it with a mock. + */ +class FakeManager extends \OCA\user_ldap\lib\user\Manager { + public function __construct() {} +} + +require_once('setup-scripts/config.php'); +$test = new IntegrationTestAccessGroupsMatchFilter($host, $port, $adn, $apwd, $bdn); +$test->init(); +$test->run(); diff --git a/tests/integration/readme.md b/tests/integration/readme.md new file mode 100644 index 000000000..e20efef8f --- /dev/null +++ b/tests/integration/readme.md @@ -0,0 +1,60 @@ +# Requirements # + +Have (as in do copy if not already done) the following files from https://github.com/owncloud/administration/tree/master/ldap-testing copied into the directory "setup-scripts": + + * start.sh + * stop.sh + * config.php + +Configure config.php according to your needs, also have a look into the LDAP and network settings in start.sh and stop.sh. + +# Usage # + +The basic command to run a test is: + +```# ./run-test.sh [phpscript]``` + +Yes, run it as root from within this directory. + +Example: + +``` +$ sudo ./run-test.sh lib/IntegrationTestAccessGroupsMatchFilter.php +71cbe88a4993e67066714d71c1cecc5ef26a54911a208103cb6294f90459e574 +c74dc0155db4efa7a0515d419528a8727bbc7596601cf25b0df05e348bd74895 +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +c74dc0155db4 osixia/phpldapadmin:0.5.1 "/sbin/my_init" 1 seconds ago Up Less than a second 80/tcp, 0.0.0.0:8443->443/tcp docker-phpldapadmin +71cbe88a4993 nickstenning/slapd:latest "/sbin/my_init" 1 seconds ago Up Less than a second 127.0.0.1:7770->389/tcp docker-slapd + +LDAP server now available under 127.0.0.1:7770 (internal IP is 172.17.0.78) +phpldapadmin now available under https://127.0.0.1:8443 + +created user : Alice Ealic +created group : RedGroup +created group : BlueGroup +created group : GreenGroup +created group : PurpleGroup +running case1 +running case2 +Tests succeeded +Stopping and resetting containers +docker-slapd +docker-phpldapadmin +docker-slapd +docker-phpldapadmin +``` + +# How it works # + +1. start.sh is executed which brings up a fresh and clean OpenLDAP in Docker. +2. The provided test script is executed. It also outputs results. +3. stop.sh is executed to shut down OpenLDAP + +# Beware # + +This is quick solution for basically one test case. With expension this mechanism should be improved as well. + +It does not run automatically, unless you do it. No integration with any testing framework. + +exceptionOnLostConnection.php is not part of this mechanism. Read its source and run it isolated. While you're at it, port it :รพ + diff --git a/tests/integration/run-test.sh b/tests/integration/run-test.sh new file mode 100755 index 000000000..e07e9b434 --- /dev/null +++ b/tests/integration/run-test.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ $1 ] ; then + TESTSCRIPT=$1 +else + echo "No test file given" exit +fi + +if [ ! -e "$TESTSCRIPT" ] ; then + echo "Test file does not exist" + exit +fi + + +# sleep is necessary, otherwise the LDAP server cannot be connected to, yet. +setup-scripts/start.sh && sleep 2 && php -f "$TESTSCRIPT" +setup-scripts/stop.sh diff --git a/tests/integration/setup-scripts/createExplicitGroups.php b/tests/integration/setup-scripts/createExplicitGroups.php new file mode 100644 index 000000000..68854de55 --- /dev/null +++ b/tests/integration/setup-scripts/createExplicitGroups.php @@ -0,0 +1,52 @@ +