diff --git a/.drone.yml b/.drone.yml index da877a95d50..2ac98e4523f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -562,6 +562,26 @@ pipeline: when: matrix: TESTS: integration-ldap-features + integration-ldap-openldap-features: + image: nextcloudci/integration-php7.0:integration-php7.0-6 + commands: + - ./occ maintenance:install --admin-pass=admin --data-dir=/dev/shm/nc_int + - ./occ app:enable user_ldap + - cd build/integration + - ./run.sh ldap_features/ldap-openldap.feature + when: + matrix: + TESTS: integration-ldap-openldap-features + integration-ldap-openldap-uid-features: + image: nextcloudci/integration-php7.0:integration-php7.0-6 + commands: + - ./occ maintenance:install --admin-pass=admin --data-dir=/dev/shm/nc_int + - ./occ app:enable user_ldap + - cd build/integration + - ./run.sh ldap_features/openldap-uid-username.feature + when: + matrix: + TESTS: integration-ldap-openldap-uid-features integration-trashbin: image: nextcloudci/integration-php7.0:integration-php7.0-8 commands: @@ -828,6 +848,10 @@ matrix: - TESTS: integration-filesdrop-features - TESTS: integration-transfer-ownership-features - TESTS: integration-ldap-features + - TESTS: integration-ldap-openldap-features + ENABLE_OPENLDAP: true + - TESTS: integration-ldap-openldap-uid-features + ENABLE_OPENLDAP: true - TESTS: integration-trashbin - TESTS: integration-remote-api - TESTS: integration-download @@ -1007,5 +1031,15 @@ services: when: matrix: TESTS: acceptance + openldap: + image: nextcloudci/openldap:openldap-4 + environment: + - SLAPD_DOMAIN=nextcloud.ci + - SLAPD_ORGANIZATION=Nextcloud + - SLAPD_PASSWORD=admin + - SLAPD_ADDITIONAL_MODULES=memberof + when: + matrix: + ENABLE_OPENLDAP: true branches: [ master, stable* ] diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index 91089e779a0..a03b4a4cb9c 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -609,23 +609,23 @@ class Access extends LDAPUtility implements IUserTools { //NOTE: mind, disabling cache affects only this instance! Using it // outside of core user management will still cache the user as non-existing. $originalTTL = $this->connection->ldapCacheTTL; - $this->connection->setConfiguration(array('ldapCacheTTL' => 0)); + $this->connection->setConfiguration(['ldapCacheTTL' => 0]); if(($isUser && $intName !== '' && !$this->ncUserManager->userExists($intName)) || (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) { if($mapper->map($fdn, $intName, $uuid)) { - $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL)); - if($this->ncUserManager instanceof PublicEmitter) { + $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]); + if($this->ncUserManager instanceof PublicEmitter && $isUser) { $this->ncUserManager->emit('\OC\User', 'assignedUserId', [$intName]); } $newlyMapped = true; return $intName; } } - $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL)); + $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]); $altName = $this->createAltInternalOwnCloudName($intName, $isUser); if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) { - if($this->ncUserManager instanceof PublicEmitter) { + if($this->ncUserManager instanceof PublicEmitter && $isUser) { $this->ncUserManager->emit('\OC\User', 'assignedUserId', [$intName]); } $newlyMapped = true; diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestAccessGroupsMatchFilter.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestAccessGroupsMatchFilter.php deleted file mode 100644 index 87c2e408424..00000000000 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestAccessGroupsMatchFilter.php +++ /dev/null @@ -1,127 +0,0 @@ - - * @author Joas Schilling - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\User_LDAP\Tests\Integration\Lib; - -use OCA\User_LDAP\Tests\Integration\AbstractIntegrationTest; - -require_once __DIR__ . '/../Bootstrap.php'; - -class IntegrationTestAccessGroupsMatchFilter extends AbstractIntegrationTest { - - /** - * prepares the LDAP environment and sets up a test configuration for - * the LDAP backend. - */ - public function init() { - require(__DIR__ . '/../setup-scripts/createExplicitUsers.php'); - require(__DIR__ . '/../setup-scripts/createExplicitGroups.php'); - require(__DIR__ . '/../setup-scripts/createExplicitGroupsDifferentOU.php'); - parent::init(); - } - - /** - * tests whether the group filter works with one specific group, while the - * input is the same. - * - * @return bool - */ - protected 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 - */ - protected 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; - } - - /** - * Tests whether a filter for limited groups is effective when more existing - * groups were passed for validation. - * - * @return bool - */ - protected function case3() { - $this->connection->setConfiguration(['ldapGroupFilter' => '(objectclass=groupOfNames)']); - - $dns = [ - 'cn=RedGroup,ou=Groups,' . $this->base, - 'cn=PurpleGroup,ou=Groups,' . $this->base, - 'cn=SquaredCircleGroup,ou=SpecialGroups,' . $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; - } - - /** - * sets up the LDAP configuration to be used for the test - */ - protected function initConnection() { - parent::initConnection(); - $this->connection->setConfiguration([ - 'ldapBaseGroups' => 'ou=Groups,' . $this->base, - 'ldapUserFilter' => 'objectclass=inetOrgPerson', - 'ldapUserDisplayName' => 'displayName', - 'ldapGroupDisplayName' => 'cn', - 'ldapLoginFilter' => 'uid=%uid', - ]); - } -} - -/** @var string $host */ -/** @var int $port */ -/** @var string $adn */ -/** @var string $apwd */ -/** @var string $bdn */ -$test = new IntegrationTestAccessGroupsMatchFilter($host, $port, $adn, $apwd, $bdn); -$test->init(); -$test->run(); diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestBackupServer.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestBackupServer.php deleted file mode 100644 index 0eef5507538..00000000000 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestBackupServer.php +++ /dev/null @@ -1,124 +0,0 @@ - - * @author Joas Schilling - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\User_LDAP\Tests\Integration\Lib; - -use OC\ServerNotAvailableException; -use OCA\User_LDAP\Tests\Integration\AbstractIntegrationTest; -use OCA\User_LDAP\Mapping\UserMapping; -use OCA\User_LDAP\User_LDAP; - -require_once __DIR__ . '/../Bootstrap.php'; - -class IntegrationTestBackupServer extends AbstractIntegrationTest { - /** @var UserMapping */ - protected $mapping; - - /** @var User_LDAP */ - protected $backend; - - /** - * sets up the LDAP configuration to be used for the test - */ - protected function initConnection() { - parent::initConnection(); - $originalHost = $this->connection->ldapHost; - $originalPort = $this->connection->ldapPort; - $this->connection->setConfiguration([ - 'ldapHost' => 'qwertz.uiop', - 'ldapPort' => '32123', - 'ldap_backup_host' => $originalHost, - 'ldap_backup_port' => $originalPort, - ]); - } - - /** - * tests that a backup connection is being used when the main LDAP server - * is offline - * - * Beware: after starting docker, the LDAP host might not be ready yet, thus - * causing a false positive. Retry in that case… or increase the sleep time - * in run-test.sh - * - * @return bool - */ - protected function case1() { - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return false; - } - return true; - } - - /** - * ensures that an exception is thrown if LDAP main server and LDAP backup - * server are not available - * - * @return bool - */ - protected function case2() { - // reset possible LDAP connection - $this->initConnection(); - try { - $this->connection->setConfiguration([ - 'ldap_backup_host' => 'qwertz.uiop', - 'ldap_backup_port' => '32123', - ]); - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return true; - } - return false; - } - - /** - * ensures that an exception is thrown if main LDAP server is down and a - * backup server is not given - * - * @return bool - */ - protected function case3() { - // reset possible LDAP connection - $this->initConnection(); - try { - $this->connection->setConfiguration([ - 'ldap_backup_host' => '', - 'ldap_backup_port' => '', - ]); - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return true; - } - return false; - } -} - -/** @var string $host */ -/** @var int $port */ -/** @var string $adn */ -/** @var string $apwd */ -/** @var string $bdn */ -$test = new IntegrationTestBackupServer($host, $port, $adn, $apwd, $bdn); -$test->init(); -$test->run(); diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestBatchApplyUserAttributes.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestBatchApplyUserAttributes.php deleted file mode 100644 index 24476c9a868..00000000000 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestBatchApplyUserAttributes.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @author Joas Schilling - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\User_LDAP\Tests\Integration\Lib; - -use OCA\User_LDAP\Mapping\UserMapping; -use OCA\User_LDAP\Tests\Integration\AbstractIntegrationTest; - -require_once __DIR__ . '/../Bootstrap.php'; - -class IntegrationTestBatchApplyUserAttributes extends AbstractIntegrationTest { - /** @var UserMapping */ - protected $mapping; - - /** - * prepares the LDAP environment and sets up a test configuration for - * the LDAP backend. - */ - public function init() { - require(__DIR__ . '/../setup-scripts/createExplicitUsers.php'); - require(__DIR__ . '/../setup-scripts/createUsersWithoutDisplayName.php'); - parent::init(); - - $this->mapping = new UserMapping(\OC::$server->getDatabaseConnection()); - $this->mapping->clear(); - $this->access->setUserMapper($this->mapping); - } - - /** - * sets up the LDAP configuration to be used for the test - */ - protected function initConnection() { - parent::initConnection(); - $this->connection->setConfiguration([ - 'ldapUserDisplayName' => 'displayname', - ]); - } - - /** - * indirectly tests whether batchApplyUserAttributes does it job properly, - * when a user without display name is included in the result set from LDAP. - * - * @return bool - */ - protected function case1() { - $result = $this->access->fetchListOfUsers('objectclass=person', 'dn'); - // on the original issue, PHP would emit a fatal error - // – cannot catch it here, but will render the test as unsuccessful - return is_array($result) && !empty($result); - } - -} - -/** @var string $host */ -/** @var int $port */ -/** @var string $adn */ -/** @var string $apwd */ -/** @var string $bdn */ -$test = new IntegrationTestBatchApplyUserAttributes($host, $port, $adn, $apwd, $bdn); -$test->init(); -$test->run(); diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestConnect.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestConnect.php deleted file mode 100644 index f4fc0f189b4..00000000000 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestConnect.php +++ /dev/null @@ -1,172 +0,0 @@ - - * @author Joas Schilling - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\User_LDAP\Tests\Integration\Lib; - -use OC\ServerNotAvailableException; -use OCA\User_LDAP\Tests\Integration\AbstractIntegrationTest; -use OCA\User_LDAP\Mapping\UserMapping; -use OCA\User_LDAP\User_LDAP; - -require_once __DIR__ . '/../Bootstrap.php'; - -class IntegrationTestConnect extends AbstractIntegrationTest { - /** @var UserMapping */ - protected $mapping; - - /** @var User_LDAP */ - protected $backend; - - /** @var string */ - protected $host; - - /** @var int */ - protected $port; - - public function __construct($host, $port, $bind, $pwd, $base) { - // make sure host is a simple host name - if(strpos($host, '://') !== false) { - $host = substr_replace($host, '', 0, strpos($host, '://') + 3); - } - if(strpos($host, ':') !== false) { - $host = substr_replace($host, '', strpos($host, ':')); - } - $this->host = $host; - $this->port = $port; - parent::__construct($host, $port, $bind, $pwd, $base); - } - - /** - * test that a faulty host will does not connect successfully - * - * @return bool - */ - protected function case1() { - // reset possible LDAP connection - $this->initConnection(); - $this->connection->setConfiguration([ - 'ldapHost' => 'qwertz.uiop', - ]); - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return true; - } - return false; - } - - /** - * tests that a connect succeeds when only a hostname is provided - * - * @return bool - */ - protected function case2() { - // reset possible LDAP connection - $this->initConnection(); - $this->connection->setConfiguration([ - 'ldapHost' => $this->host, - ]); - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return false; - } - return true; - } - - /** - * tests that a connect succeeds when an LDAP URL is provided - * - * @return bool - */ - protected function case3() { - // reset possible LDAP connection - $this->initConnection(); - $this->connection->setConfiguration([ - 'ldapHost' => 'ldap://' . $this->host, - ]); - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return false; - } - return true; - } - - /** - * tests that a connect succeeds when an LDAP URL with port is provided - * - * @return bool - */ - protected function case4() { - // reset possible LDAP connection - $this->initConnection(); - $this->connection->setConfiguration([ - 'ldapHost' => 'ldap://' . $this->host . ':' . $this->port, - ]); - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return false; - } - return true; - } - - /** - * tests that a connect succeeds when a hostname with port is provided - * - * @return bool - */ - protected function case5() { - // reset possible LDAP connection - $this->initConnection(); - $this->connection->setConfiguration([ - 'ldapHost' => $this->host . ':' . $this->port, - ]); - try { - $this->connection->getConnectionResource(); - } catch (ServerNotAvailableException $e) { - return false; - } - return true; - } - - /** - * repeat case1, only to make sure that not a connection was reused by - * accident. - * - * @return bool - */ - protected function case6() { - return $this->case1(); - } -} - -/** @var string $host */ -/** @var int $port */ -/** @var string $adn */ -/** @var string $apwd */ -/** @var string $bdn */ -$test = new IntegrationTestConnect($host, $port, $adn, $apwd, $bdn); -$test->init(); -$test->run(); diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestPaging.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestPaging.php index d54d001c4ad..fcb2e59b4a9 100644 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestPaging.php +++ b/apps/user_ldap/tests/Integration/Lib/IntegrationTestPaging.php @@ -58,30 +58,12 @@ class IntegrationTestPaging extends AbstractIntegrationTest { ]); } - /** - * tests that paging works properly against a simple example (reading all - * of few users in small steps) - * - * @return bool - */ - protected function case1() { - $filter = 'objectclass=inetorgperson'; - $attributes = ['cn', 'dn']; - - $result = $this->access->searchUsers($filter, $attributes); - if(count($result) === 7) { - return true; - } - - return false; - } - /** * fetch first three, afterwards all users * * @return bool */ - protected function case2() { + protected function case1() { $filter = 'objectclass=inetorgperson'; $attributes = ['cn', 'dn']; @@ -102,23 +84,6 @@ class IntegrationTestPaging extends AbstractIntegrationTest { return true; } - - /** - * reads all remaining users starting first page - * - * @return bool - */ - protected function case3() { - $filter = 'objectclass=inetorgperson'; - $attributes = ['cn', 'dn']; - - $result = $this->access->searchUsers($filter, $attributes, null, $this->pagingSize); - if(count($result) === (7 - $this->pagingSize)) { - return true; - } - - return false; - } } /** @var string $host */ diff --git a/apps/user_ldap/tests/Integration/Lib/IntegrationTestUserHome.php b/apps/user_ldap/tests/Integration/Lib/IntegrationTestUserHome.php deleted file mode 100644 index 9ee5a7efac2..00000000000 --- a/apps/user_ldap/tests/Integration/Lib/IntegrationTestUserHome.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Joas Schilling - * @author Jörn Friedrich Dreyer - * @author Morris Jobke - * @author Vinicius Cubas Brand - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OCA\User_LDAP\Tests\Integration\Lib; - -use OCA\User_LDAP\FilesystemHelper; -use OCA\User_LDAP\LogWrapper; -use OCA\User_LDAP\User\Manager as LDAPUserManager; -use OCA\User_LDAP\Tests\Integration\AbstractIntegrationTest; -use OCA\User_LDAP\Mapping\UserMapping; -use OCA\User_LDAP\User_LDAP; -use OCP\Image; - -require_once __DIR__ . '/../Bootstrap.php'; - -class IntegrationTestUserHome extends AbstractIntegrationTest { - /** @var UserMapping */ - protected $mapping; - - /** @var User_LDAP */ - protected $backend; - - /** - * prepares the LDAP environment and sets up a test configuration for - * the LDAP backend. - */ - public function init() { - require(__DIR__ . '/../setup-scripts/createExplicitUsers.php'); - parent::init(); - - $this->mapping = new UserMapping(\OC::$server->getDatabaseConnection()); - $this->mapping->clear(); - $this->access->setUserMapper($this->mapping); - $this->backend = new User_LDAP($this->access, \OC::$server->getConfig(), \OC::$server->getNotificationManager(), \OC::$server->getUserSession(), \OC::$server->query('LDAPUserPluginManager')); - } - - /** - * sets up the LDAP configuration to be used for the test - */ - protected function initConnection() { - parent::initConnection(); - $this->connection->setConfiguration([ - 'homeFolderNamingRule' => 'homeDirectory', - ]); - } - - /** - * initializes an LDAP user manager instance - * @return LDAPUserManager - */ - protected function initUserManager() { - $this->userManager = new LDAPUserManager( - \OC::$server->getConfig(), - new FilesystemHelper(), - new LogWrapper(), - \OC::$server->getAvatarManager(), - new Image(), - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserManager(), - \OC::$server->getNotificationManager() - ); - } - - /** - * homeDirectory on LDAP is empty. Return values of getHome should be - * identical to user name, following Nextcloud default. - * - * @return bool - */ - protected function case1() { - \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); - $userManager = \OC::$server->getUserManager(); - $userManager->clearBackends(); - $userManager->registerBackend($this->backend); - $users = $userManager->search('', 5, 0); - - foreach($users as $user) { - $home = $user->getHome(); - $uid = $user->getUID(); - $posFound = strpos($home, '/' . $uid); - $posExpected = strlen($home) - (strlen($uid) + 1); - if($posFound === false || $posFound !== $posExpected) { - print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); - return false; - } - } - - return true; - } - - /** - * homeDirectory on LDAP is empty. Having the attributes set is enforced. - * - * @return bool - */ - protected function case2() { - \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); - $userManager = \OC::$server->getUserManager(); - // clearing backends is critical, otherwise the userManager will have - // the user objects cached and the value from case1 returned - $userManager->clearBackends(); - $userManager->registerBackend($this->backend); - $users = $userManager->search('', 5, 0); - - try { - foreach ($users as $user) { - $user->getHome(); - print('User home was retrieved without throwing an Exception!' . PHP_EOL); - return false; - } - } catch (\Exception $e) { - if(strpos($e->getMessage(), 'Home dir attribute') === 0) { - return true; - } - } - - return false; - } - - /** - * homeDirectory on LDAP is set to "attr:" which is effectively empty. - * Return values of getHome should be Nextcloud default. - * - * @return bool - */ - protected function case3() { - \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', true); - $this->connection->setConfiguration([ - 'homeFolderNamingRule' => 'attr:', - ]); - $userManager = \OC::$server->getUserManager(); - $userManager->clearBackends(); - $userManager->registerBackend($this->backend); - $users = $userManager->search('', 5, 0); - - try { - foreach ($users as $user) { - $home = $user->getHome(); - $uid = $user->getUID(); - $posFound = strpos($home, '/' . $uid); - $posExpected = strlen($home) - (strlen($uid) + 1); - if ($posFound === false || $posFound !== $posExpected) { - print('"' . $user->getUID() . '" was not found in "' . $home . '" or does not end with it.' . PHP_EOL); - return false; - } - } - } catch (\Exception $e) { - print("Unexpected Exception: " . $e->getMessage() . PHP_EOL); - return false; - } - - return true; - } -} - -/** @var string $host */ -/** @var int $port */ -/** @var string $adn */ -/** @var string $apwd */ -/** @var string $bdn */ -$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn); -$test->init(); -$test->run(); diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php index 32e02bad2a3..f6c93aa5174 100644 --- a/build/integration/features/bootstrap/BasicStructure.php +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -497,4 +497,11 @@ trait BasicStructure { $file->isDir() ? rmdir($file) : unlink($file); } } + + /** + * @Given /^cookies are reset$/ + */ + public function cookiesAreReset() { + $this->cookieJar = new CookieJar(); + } } diff --git a/build/integration/features/bootstrap/LDAPContext.php b/build/integration/features/bootstrap/LDAPContext.php index e2b30011515..ee7acab6f5f 100644 --- a/build/integration/features/bootstrap/LDAPContext.php +++ b/build/integration/features/bootstrap/LDAPContext.php @@ -32,6 +32,14 @@ class LDAPContext implements Context { protected $apiUrl; + /** @AfterScenario */ + public function teardown() { + if($this->configID === null) { + return; + } + $this->sendingTo('DELETE', $this->apiUrl . '/' . $this->configID); + } + /** * @Given /^the response should contain a tag "([^"]*)"$/ */ @@ -82,4 +90,110 @@ class LDAPContext implements Context { public function settingTheLDAPConfigurationTo(TableNode $configData) { $this->sendingToWith('PUT', $this->apiUrl . '/' . $this->configID, $configData); } + + /** + * @Given /^having a valid LDAP configuration$/ + */ + public function havingAValidLDAPConfiguration() { + $this->asAn('admin'); + $this->creatingAnLDAPConfigurationAt('/apps/user_ldap/api/v1/config'); + $data = new TableNode([ + ['configData[ldapHost]', 'openldap'], + ['configData[ldapPort]', '389'], + ['configData[ldapBase]', 'dc=nextcloud,dc=ci'], + ['configData[ldapAgentName]', 'cn=admin,dc=nextcloud,dc=ci'], + ['configData[ldapAgentPassword]', 'admin'], + ['configData[ldapUserFilter]', '(&(objectclass=inetorgperson))'], + ['configData[ldapLoginFilter]', '(&(objectclass=inetorgperson)(uid=%uid))'], + ['configData[ldapUserDisplayName]', 'displayname'], + ['configData[ldapGroupDisplayName]', 'cn'], + ['configData[ldapEmailAttribute]', 'mail'], + ['configData[ldapConfigurationActive]', '1'], + ]); + $this->settingTheLDAPConfigurationTo($data); + $this->asAn(''); + } + + /** + * @Given /^looking up details for the first result matches expectations$/ + * @param TableNode $expectations + */ + public function lookingUpDetailsForTheFirstResult(TableNode $expectations) { + $userResultElements = simplexml_load_string($this->response->getBody())->data[0]->users[0]->element; + $userResults = json_decode(json_encode($userResultElements), 1); + $userId = array_shift($userResults); + + $this->sendingTo('GET', '/cloud/users/' . $userId); + $this->theRecordFieldsShouldMatch($expectations); + } + + /** + * @Given /^modify LDAP configuration$/ + */ + public function modifyLDAPConfiguration(TableNode $table) { + $originalAsAn = $this->currentUser; + $this->asAn('admin'); + $configData = $table->getRows(); + foreach($configData as &$row) { + $row[0] = 'configData[' . $row[0] . ']'; + } + $this->settingTheLDAPConfigurationTo(new TableNode($configData)); + $this->asAn($originalAsAn); + } + + /** + * @Given /^the "([^"]*)" result should match$/ + */ + public function theGroupResultShouldMatch(string $type, TableNode $expectations) { + $listReturnedElements = simplexml_load_string($this->response->getBody())->data[0]->$type[0]->element; + $extractedIDsArray = json_decode(json_encode($listReturnedElements), 1); + foreach($expectations->getRows() as $expectation) { + if((int)$expectation[1] === 1) { + Assert::assertContains($expectation[0], $extractedIDsArray); + } else { + Assert::assertNotContains($expectation[0], $extractedIDsArray); + } + } + } + + /** + * @Given /^Expect ServerException on failed web login as "([^"]*)"$/ + */ + public function expectServerExceptionOnFailedWebLoginAs($login) { + try { + $this->loggingInUsingWebAs($login); + } catch (\GuzzleHttp\Exception\ServerException $e) { + Assert::assertEquals(500, $e->getResponse()->getStatusCode()); + return; + } + Assert::assertTrue(false, 'expected Exception not received'); + } + + /** + * @Given /^the "([^"]*)" result should contain "([^"]*)" of$/ + */ + public function theResultShouldContainOf($type, $expectedCount, TableNode $expectations) { + $listReturnedElements = simplexml_load_string($this->response->getBody())->data[0]->$type[0]->element; + $extractedIDsArray = json_decode(json_encode($listReturnedElements), 1); + $uidsFound = 0; + foreach($expectations->getRows() as $expectation) { + if(in_array($expectation[0], $extractedIDsArray)) { + $uidsFound++; + } + } + Assert::assertSame((int)$expectedCount, $uidsFound); + } + + /** + * @Given /^the record's fields should match$/ + */ + public function theRecordFieldsShouldMatch(TableNode $expectations) { + foreach($expectations->getRowsHash() as $k => $v) { + $value = (string)simplexml_load_string($this->response->getBody())->data[0]->$k; + Assert::assertEquals($v, $value, "got $value"); + } + + $backend = (string)simplexml_load_string($this->response->getBody())->data[0]->backend; + Assert::assertEquals('LDAP', $backend); + } } diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index 8e65f4c1a0e..10b4c1bc005 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -342,6 +342,7 @@ Feature: provisioning | theming | | twofactor_backupcodes | | updatenotification | + | user_ldap | | workflowengine | | files_external | | oauth2 | diff --git a/build/integration/ldap_features/ldap-openldap.feature b/build/integration/ldap_features/ldap-openldap.feature new file mode 100644 index 00000000000..4c507e74595 --- /dev/null +++ b/build/integration/ldap_features/ldap-openldap.feature @@ -0,0 +1,104 @@ +Feature: LDAP + Background: + Given using api version "2" + And having a valid LDAP configuration + + Scenario: Test valid configuration by logging in + Given Logging in using web as "alice" + And Sending a "GET" to "/remote.php/webdav/welcome.txt" with requesttoken + Then the HTTP status code should be "200" + + Scenario: Test valid configuration with port in the hostname by logging in + Given modify LDAP configuration + | ldapHost | openldap:389 | + And cookies are reset + And Logging in using web as "alice" + And Sending a "GET" to "/remote.php/webdav/welcome.txt" with requesttoken + Then the HTTP status code should be "200" + + Scenario: Test valid configuration with LDAP protocol by logging in + Given modify LDAP configuration + | ldapHost | ldap://openldap | + And cookies are reset + And Logging in using web as "alice" + And Sending a "GET" to "/remote.php/webdav/welcome.txt" with requesttoken + Then the HTTP status code should be "200" + + Scenario: Test valid configuration with LDAP protoccol and port by logging in + Given modify LDAP configuration + | ldapHost | ldap://openldap:389 | + And cookies are reset + And Logging in using web as "alice" + And Sending a "GET" to "/remote.php/webdav/welcome.txt" with requesttoken + Then the HTTP status code should be "200" + + Scenario: Look for a known LDAP user + Given As an "admin" + And sending "GET" to "/cloud/users?search=alice" + Then the OCS status code should be "200" + And looking up details for the first result matches expectations + | email | alice@nextcloud.ci | + | displayname | Alice | + + Scenario: Test group filter with one specific group + Given modify LDAP configuration + | ldapGroupFilter | cn=RedGroup | + | ldapBaseGroups | ou=Groups,ou=Ordinary,dc=nextcloud,dc=ci | + And As an "admin" + And sending "GET" to "/cloud/groups" + Then the OCS status code should be "200" + And the "groups" result should match + | RedGroup | 1 | + | GreenGroup | 0 | + | BlueGroup | 0 | + | PurpleGroup | 0 | + + Scenario: Test group filter with two specific groups + Given modify LDAP configuration + | ldapGroupFilter | (\|(cn=RedGroup)(cn=GreenGroup)) | + | ldapBaseGroups | ou=Groups,ou=Ordinary,dc=nextcloud,dc=ci | + And As an "admin" + And sending "GET" to "/cloud/groups" + Then the OCS status code should be "200" + And the "groups" result should match + | RedGroup | 1 | + | GreenGroup | 1 | + | BlueGroup | 0 | + | PurpleGroup | 0 | + + Scenario: Test group filter ruling out a group from a different base + Given modify LDAP configuration + | ldapGroupFilter | (objectClass=groupOfNames) | + | ldapBaseGroups | ou=Groups,ou=Ordinary,dc=nextcloud,dc=ci | + And As an "admin" + And sending "GET" to "/cloud/groups" + Then the OCS status code should be "200" + And the "groups" result should match + | RedGroup | 1 | + | GreenGroup | 1 | + | BlueGroup | 1 | + | PurpleGroup | 1 | + | SquareGroup | 0 | + + Scenario: Test backup server + Given modify LDAP configuration + | ldapBackupHost | openldap | + | ldapBackupPort | 389 | + | ldapHost | foo.bar | + | ldapPort | 2456 | + And Logging in using web as "alice" + Then the HTTP status code should be "200" + + Scenario: Test backup server offline + Given modify LDAP configuration + | ldapBackupHost | off.line | + | ldapBackupPort | 3892 | + | ldapHost | foo.bar | + | ldapPort | 2456 | + Then Expect ServerException on failed web login as "alice" + + Scenario: Test LDAP server offline, no backup server + Given modify LDAP configuration + | ldapHost | foo.bar | + | ldapPort | 2456 | + Then Expect ServerException on failed web login as "alice" diff --git a/build/integration/ldap_features/openldap-uid-username.feature b/build/integration/ldap_features/openldap-uid-username.feature new file mode 100644 index 00000000000..d267870ca26 --- /dev/null +++ b/build/integration/ldap_features/openldap-uid-username.feature @@ -0,0 +1,88 @@ +Feature: LDAP + Background: + Given using api version "2" + And having a valid LDAP configuration + And modify LDAP configuration + | ldapExpertUsernameAttr | uid | + + Scenario: Look for a expected LDAP users + Given As an "admin" + And sending "GET" to "/cloud/users" + Then the OCS status code should be "200" + And the "users" result should match + | alice | 1 | + | elisa | 1 | + | ghost | 0 | + + Scenario: check default home of an LDAP user + Given As an "admin" + And sending "GET" to "/cloud/users/alice" + Then the OCS status code should be "200" + And the record's fields should match + | storageLocation | /dev/shm/nc_int/alice | + + Scenario: check custom relative home of an LDAP user + Given modify LDAP configuration + | homeFolderNamingRule | sn | + And As an "admin" + And sending "GET" to "/cloud/users/alice" + Then the OCS status code should be "200" + And the record's fields should match + | storageLocation | /dev/shm/nc_int/Alfgeirdottir | + + Scenario: check custom absolute home of an LDAP user + Given modify LDAP configuration + | homeFolderNamingRule | roomNumber | + And As an "admin" + And sending "GET" to "/cloud/users/elisa" + Then the OCS status code should be "200" + And the record's fields should match + | storageLocation | /dev/shm/elisa-data | + + Scenario: Fetch all users, invoking pagination + Given modify LDAP configuration + | ldapBaseUsers | ou=PagingTest,dc=nextcloud,dc=ci | + | ldapPagingSize | 2 | + And As an "admin" + And sending "GET" to "/cloud/users" + Then the OCS status code should be "200" + And the "users" result should match + | ebba | 1 | + | eindis | 1 | + | fjolnir | 1 | + | gunna | 1 | + | juliana | 1 | + | leo | 1 | + | stigur | 1 | + + Scenario: Fetch all users, invoking pagination + Given modify LDAP configuration + | ldapBaseUsers | ou=PagingTest,dc=nextcloud,dc=ci | + | ldapPagingSize | 2 | + And As an "admin" + And sending "GET" to "/cloud/users?limit=10" + Then the OCS status code should be "200" + And the "users" result should match + | ebba | 1 | + | eindis | 1 | + | fjolnir | 1 | + | gunna | 1 | + | juliana | 1 | + | leo | 1 | + | stigur | 1 | + + Scenario: Fetch from second batch of all users, invoking pagination + Given modify LDAP configuration + | ldapBaseUsers | ou=PagingTest,dc=nextcloud,dc=ci | + | ldapPagingSize | 2 | + And As an "admin" + And sending "GET" to "/cloud/users?limit=10&offset=2" + Then the OCS status code should be "200" + And the "users" result should contain "5" of + | ebba | + | eindis | + | fjolnir | + | gunna | + | juliana | + | leo | + | stigur | diff --git a/build/integration/run.sh b/build/integration/run.sh index b747bb52c6b..56f4ee7b07d 100755 --- a/build/integration/run.sh +++ b/build/integration/run.sh @@ -22,6 +22,7 @@ else exit 1 fi fi +NC_DATADIR=$($OCC config:system:get datadirectory) composer install @@ -48,6 +49,7 @@ if [ "$INSTALLED" == "true" ]; then #Enable external storage app $OCC app:enable files_external + $OCC app:enable user_ldap mkdir -p work/local_storage OUTPUT_CREATE_STORAGE=`$OCC files_external:create local_storage local null::null -c datadir=$PWD/work/local_storage` @@ -70,10 +72,11 @@ if [ "$INSTALLED" == "true" ]; then #Disable external storage app $OCC app:disable files_external + $OCC app:disable user_ldap fi if [ -z $HIDE_OC_LOGS ]; then - tail "${OC_PATH}/data/nextcloud.log" + tail "${NC_DATADIR}/nextcloud.log" fi echo "runsh: Exit code: $RESULT"