From 02ec8b1726eb867e88dd2c31a74a080e451a31d1 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 Jul 2016 16:46:29 +0800 Subject: [PATCH] New LDAPProvider for user_ldap --- apps/user_ldap/ajax/wizard.php | 2 +- apps/user_ldap/appinfo/app.php | 2 +- apps/user_ldap/appinfo/install.php | 4 + apps/user_ldap/appinfo/update.php | 22 ++ apps/user_ldap/lib/Access.php | 73 +--- apps/user_ldap/lib/Connection.php | 6 +- apps/user_ldap/lib/Helper.php | 65 ++++ apps/user_ldap/lib/IUserLDAP.php | 48 +++ apps/user_ldap/lib/Jobs/UpdateGroups.php | 2 +- apps/user_ldap/lib/LDAPProvider.php | 187 ++++++++++ apps/user_ldap/lib/LDAPProviderFactory.php | 57 +++ apps/user_ldap/lib/Proxy.php | 2 +- apps/user_ldap/lib/User_LDAP.php | 35 +- apps/user_ldap/lib/User_Proxy.php | 32 +- apps/user_ldap/tests/AccessTest.php | 51 +-- apps/user_ldap/tests/Group_LDAPTest.php | 3 +- .../Integration/AbstractIntegrationTest.php | 14 +- apps/user_ldap/tests/Jobs/CleanUpTest.php | 2 +- apps/user_ldap/tests/LDAPProviderTest.php | 337 ++++++++++++++++++ apps/user_ldap/tests/User/UserTest.php | 3 +- apps/user_ldap/tests/User_LDAPTest.php | 4 +- apps/user_ldap/tests/WizardTest.php | 3 +- lib/private/Server.php | 19 + lib/public/LDAP/IDeletionFlagSupport.php | 44 +++ lib/public/LDAP/ILDAPProvider.php | 104 ++++++ lib/public/LDAP/ILDAPProviderFactory.php | 51 +++ 26 files changed, 1074 insertions(+), 98 deletions(-) create mode 100644 apps/user_ldap/appinfo/update.php create mode 100644 apps/user_ldap/lib/IUserLDAP.php create mode 100644 apps/user_ldap/lib/LDAPProvider.php create mode 100644 apps/user_ldap/lib/LDAPProviderFactory.php create mode 100644 apps/user_ldap/tests/LDAPProviderTest.php create mode 100644 lib/public/LDAP/IDeletionFlagSupport.php create mode 100644 lib/public/LDAP/ILDAPProvider.php create mode 100644 lib/public/LDAP/ILDAPProviderFactory.php diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php index b5eab90af4..654fb70ced 100644 --- a/apps/user_ldap/ajax/wizard.php +++ b/apps/user_ldap/ajax/wizard.php @@ -60,7 +60,7 @@ $userManager = new \OCA\User_LDAP\User\Manager( \OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()); -$access = new \OCA\User_LDAP\Access($con, $ldapWrapper, $userManager); +$access = new \OCA\User_LDAP\Access($con, $ldapWrapper, $userManager, new \OCA\User_LDAP\Helper()); $wizard = new \OCA\User_LDAP\Wizard($configuration, $ldapWrapper, $access); diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 18987614bd..10cc003a3f 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -44,7 +44,7 @@ if(count($configPrefixes) === 1) { \OC::$server->getUserManager() ); $connector = new OCA\User_LDAP\Connection($ldapWrapper, $configPrefixes[0]); - $ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager); + $ldapAccess = new OCA\User_LDAP\Access($connector, $ldapWrapper, $userManager, $helper); $ldapAccess->setUserMapper(new OCA\User_LDAP\Mapping\UserMapping($dbc)); $ldapAccess->setGroupMapper(new OCA\User_LDAP\Mapping\GroupMapping($dbc)); diff --git a/apps/user_ldap/appinfo/install.php b/apps/user_ldap/appinfo/install.php index b3c92b0024..c16a1f4a03 100644 --- a/apps/user_ldap/appinfo/install.php +++ b/apps/user_ldap/appinfo/install.php @@ -4,6 +4,7 @@ * * @author Arthur Schiwon * @author Christopher Schäpers + * @author Roger Szabo * * @license AGPL-3.0 * @@ -24,3 +25,6 @@ $state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'doSet'); if($state === 'doSet') { OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); } + +$helper = new \OCA\User_LDAP\Helper(); +$helper->setLDAPProvider(); diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php new file mode 100644 index 0000000000..f816a7ec9c --- /dev/null +++ b/apps/user_ldap/appinfo/update.php @@ -0,0 +1,22 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ +$helper = new \OCA\User_LDAP\Helper(); +$helper->setLDAPProvider(); diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index dabf243eda..299ad58164 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -20,6 +20,7 @@ * @author Ralph Krimmel * @author Renaud Fortier * @author Robin McCorkell + * @author Roger Szabo * * @license AGPL-3.0 * @@ -77,13 +78,19 @@ class Access extends LDAPUtility implements IUserTools { * @var AbstractMapping $userMapper */ protected $groupMapper; + + /** + * @var \OCA\User_LDAP\Helper + */ + private $helper; public function __construct(Connection $connection, ILDAPWrapper $ldap, - Manager $userManager) { + Manager $userManager, Helper $helper) { parent::__construct($ldap); $this->connection = $connection; $this->userManager = $userManager; $this->userManager->setLdapAccess($this); + $this->helper = $helper; } /** @@ -173,7 +180,7 @@ class Access extends LDAPUtility implements IUserTools { // (cf. #12306), 500 is default for paging and should work everywhere. $maxResults = $pagingSize > 20 ? $pagingSize : 500; $this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0); - $dn = $this->DNasBaseParameter($dn); + $dn = $this->helper->DNasBaseParameter($dn); $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); if(!$this->ldap->isResource($rr)) { if(!empty($attr)) { @@ -201,7 +208,7 @@ class Access extends LDAPUtility implements IUserTools { $values = array(); for($i=0;$i<$result[$attr]['count'];$i++) { if($this->resemblesDN($attr)) { - $values[] = $this->sanitizeDN($result[$attr][$i]); + $values[] = $this->helper->sanitizeDN($result[$attr][$i]); } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') { $values[] = $this->convertObjectGUID2Str($result[$attr][$i]); } else { @@ -242,49 +249,6 @@ class Access extends LDAPUtility implements IUserTools { return (is_array($r) && count($r) > 1); } - /** - * sanitizes a DN received from the LDAP server - * @param array $dn the DN in question - * @return array the sanitized DN - */ - private function sanitizeDN($dn) { - //treating multiple base DNs - if(is_array($dn)) { - $result = array(); - foreach($dn as $singleDN) { - $result[] = $this->sanitizeDN($singleDN); - } - return $result; - } - - //OID sometimes gives back DNs with whitespace after the comma - // a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); - - //make comparisons and everything work - $dn = mb_strtolower($dn, 'UTF-8'); - - //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn - //to use the DN in search filters, \ needs to be escaped to \5c additionally - //to use them in bases, we convert them back to simple backslashes in readAttribute() - $replacements = array( - '\,' => '\5c2C', - '\=' => '\5c3D', - '\+' => '\5c2B', - '\<' => '\5c3C', - '\>' => '\5c3E', - '\;' => '\5c3B', - '\"' => '\5c22', - '\#' => '\5c23', - '(' => '\28', - ')' => '\29', - '*' => '\2A', - ); - $dn = str_replace(array_keys($replacements), array_values($replacements), $dn); - - return $dn; - } - /** * returns a DN-string that is cleaned from not domain parts, e.g. * cn=foo,cn=bar,dc=foobar,dc=server,dc=org @@ -1071,10 +1035,10 @@ class Access extends LDAPUtility implements IUserTools { } if($key !== 'dn') { $selection[$i][$key] = $this->resemblesDN($key) ? - $this->sanitizeDN($item[$key]) + $this->helper->sanitizeDN($item[$key]) : $item[$key]; } else { - $selection[$i][$key] = [$this->sanitizeDN($item[$key])]; + $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])]; } } @@ -1298,7 +1262,7 @@ class Access extends LDAPUtility implements IUserTools { * @return bool */ public function areCredentialsValid($name, $password) { - $name = $this->DNasBaseParameter($name); + $name = $this->helper->DNasBaseParameter($name); $testConnection = clone $this->connection; $credentials = array( 'ldapAgentName' => $name, @@ -1569,15 +1533,6 @@ class Access extends LDAPUtility implements IUserTools { return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs)); } - /** - * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters - * @param string $dn the DN - * @return string - */ - private function DNasBaseParameter($dn) { - return str_ireplace('\\5c', '\\', $dn); - } - /** * checks if the given DN is part of the given base DN(s) * @param string $dn the DN @@ -1586,7 +1541,7 @@ class Access extends LDAPUtility implements IUserTools { */ public function isDNPartOfBase($dn, $bases) { $belongsToBase = false; - $bases = $this->sanitizeDN($bases); + $bases = $this->helper->sanitizeDN($bases); foreach($bases as $base) { $belongsToBase = true; diff --git a/apps/user_ldap/lib/Connection.php b/apps/user_ldap/lib/Connection.php index 7bd5e97e4f..7fb2652619 100644 --- a/apps/user_ldap/lib/Connection.php +++ b/apps/user_ldap/lib/Connection.php @@ -11,6 +11,7 @@ * @author Morris Jobke * @author Robin Appelman * @author Robin McCorkell + * @author Roger Szabo * * @license AGPL-3.0 * @@ -52,6 +53,8 @@ class Connection extends LDAPUtility { private $configID; private $configured = false; private $hasPagedResultSupport = true; + //whether connection should be kept on __destruct + private $dontDestruct = false; /** * @var bool runtime flag that indicates whether supported primary groups are available @@ -93,7 +96,7 @@ class Connection extends LDAPUtility { } public function __destruct() { - if($this->ldap->isResource($this->ldapConnectionRes)) { + if(!$this->dontDestruct && $this->ldap->isResource($this->ldapConnectionRes)) { @$this->ldap->unbind($this->ldapConnectionRes); }; } @@ -105,6 +108,7 @@ class Connection extends LDAPUtility { $this->configuration = new Configuration($this->configPrefix, !is_null($this->configID)); $this->ldapConnectionRes = null; + $this->dontDestruct = true; } /** diff --git a/apps/user_ldap/lib/Helper.php b/apps/user_ldap/lib/Helper.php index ccc1d2c0b4..90807a3c52 100644 --- a/apps/user_ldap/lib/Helper.php +++ b/apps/user_ldap/lib/Helper.php @@ -10,6 +10,7 @@ * @author Morris Jobke * @author Thomas Müller * @author Vincent Petry + * @author Roger Szabo * * @license AGPL-3.0 * @@ -183,6 +184,70 @@ class Helper { return $domain; } + + /** + * + * Set the LDAPProvider in the config + * + */ + public function setLDAPProvider() { + $current = \OC::$server->getConfig()->getSystemValue('ldapProviderFactory', null); + if(is_null($current)) { + \OC::$server->getConfig()->setSystemValue('ldapProviderFactory', '\\OCA\\User_LDAP\\LDAPProviderFactory'); + } + } + + /** + * sanitizes a DN received from the LDAP server + * @param array $dn the DN in question + * @return array the sanitized DN + */ + public function sanitizeDN($dn) { + //treating multiple base DNs + if(is_array($dn)) { + $result = array(); + foreach($dn as $singleDN) { + $result[] = $this->sanitizeDN($singleDN); + } + return $result; + } + + //OID sometimes gives back DNs with whitespace after the comma + // a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn + //to use the DN in search filters, \ needs to be escaped to \5c additionally + //to use them in bases, we convert them back to simple backslashes in readAttribute() + $replacements = array( + '\,' => '\5c2C', + '\=' => '\5c3D', + '\+' => '\5c2B', + '\<' => '\5c3C', + '\>' => '\5c3E', + '\;' => '\5c3B', + '\"' => '\5c22', + '\#' => '\5c23', + '(' => '\28', + ')' => '\29', + '*' => '\2A', + ); + $dn = str_replace(array_keys($replacements), array_values($replacements), $dn); + + return $dn; + } + + /** + * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters + * @param string $dn the DN + * @return string + */ + public function DNasBaseParameter($dn) { + return str_ireplace('\\5c', '\\', $dn); + } /** * listens to a hook thrown by server2server sharing and replaces the given diff --git a/apps/user_ldap/lib/IUserLDAP.php b/apps/user_ldap/lib/IUserLDAP.php new file mode 100644 index 0000000000..c04e2ddffe --- /dev/null +++ b/apps/user_ldap/lib/IUserLDAP.php @@ -0,0 +1,48 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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; + +interface IUserLDAP { + + //Functions used by LDAPProvider + + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid); + + /** + * Return a new LDAP connection for the specified user. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid); + + /** + * Return the username for the given LDAP DN, if available. + * @param string $dn + * @return string|false with the name to use in ownCloud + */ + public function dn2UserName($dn); +} diff --git a/apps/user_ldap/lib/Jobs/UpdateGroups.php b/apps/user_ldap/lib/Jobs/UpdateGroups.php index 91d40d5874..047b95a6d9 100644 --- a/apps/user_ldap/lib/Jobs/UpdateGroups.php +++ b/apps/user_ldap/lib/Jobs/UpdateGroups.php @@ -188,7 +188,7 @@ class UpdateGroups extends \OC\BackgroundJob\TimedJob { $dbc, \OC::$server->getUserManager()); $connector = new Connection($ldapWrapper, $configPrefixes[0]); - $ldapAccess = new Access($connector, $ldapWrapper, $userManager); + $ldapAccess = new Access($connector, $ldapWrapper, $userManager, $helper); $groupMapper = new GroupMapping($dbc); $userMapper = new UserMapping($dbc); $ldapAccess->setGroupMapper($groupMapper); diff --git a/apps/user_ldap/lib/LDAPProvider.php b/apps/user_ldap/lib/LDAPProvider.php new file mode 100644 index 0000000000..8d6b560059 --- /dev/null +++ b/apps/user_ldap/lib/LDAPProvider.php @@ -0,0 +1,187 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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; + +use OCP\IUserBackend; +use OCP\LDAP\ILDAPProvider; +use OCP\LDAP\IDeletionFlagSupport; +use OCP\IServerContainer; +use OCA\User_LDAP\User\DeletedUsersIndex; +use OCA\User_LDAP\Mapping\UserMapping; + +/** + * LDAP provider for pulic access to the LDAP backend. + */ +class LDAPProvider implements ILDAPProvider, IDeletionFlagSupport { + + private $backend; + private $logger; + private $helper; + private $deletedUsersIndex; + + /** + * Create new LDAPProvider + * @param \OCP\IServerContainer $serverContainer + * @throws \Exception if user_ldap app was not enabled + */ + public function __construct(IServerContainer $serverContainer, Helper $helper, DeletedUsersIndex $deletedUsersIndex) { + $this->logger = $serverContainer->getLogger(); + $this->helper = $helper; + $this->deletedUsersIndex = $deletedUsersIndex; + foreach ($serverContainer->getUserManager()->getBackends() as $backend){ + $this->logger->debug('instance '.get_class($backend).' backend.', ['app' => 'user_ldap']); + if ($backend instanceof IUserLDAP) { + $this->backend = $backend; + return; + } + } + throw new \Exception('To use the LDAPProvider, user_ldap app must be enabled'); + } + + /** + * Translate an ownCloud user id to LDAP DN + * @param string $uid ownCloud user id + * @return string with the LDAP DN + * @throws \Exception if translation was unsuccessful + */ + public function getUserDN($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + $result = $this->backend->getLDAPAccess($uid)->username2dn($uid); + if(!$result){ + throw new \Exception('Translation to LDAP DN unsuccessful'); + } + return $result; + } + + /** + * Translate a LDAP DN to an ownCloud user name. If there is no mapping between + * the DN and the user name, a new one will be created. + * @param string $dn LDAP DN + * @return string with the ownCloud user name + * @throws \Exception if translation was unsuccessful + */ + public function getUserName($dn) { + $result = $this->backend->dn2UserName($dn); + if(!$result){ + throw new \Exception('Translation to ownCloud user name unsuccessful'); + } + return $result; + } + + /** + * Convert a stored DN so it can be used as base parameter for LDAP queries. + * @param string $dn the DN in question + * @return string + */ + public function DNasBaseParameter($dn) { + return $this->helper->DNasBaseParameter($dn); + } + + /** + * Sanitize a DN received from the LDAP server. + * @param array $dn the DN in question + * @return array the sanitized DN + */ + public function sanitizeDN($dn) { + return $this->helper->sanitizeDN($dn); + } + + /** + * Return a new LDAP connection resource for the specified user. + * The connection must be closed manually. + * @param string $uid ownCloud user id + * @return resource of the LDAP connection + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPConnection($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getNewLDAPConnection($uid); + } + + /** + * Get the LDAP base for users. + * @param string $uid ownCloud user id + * @return string the base for users + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPBaseUsers($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getLDAPAccess($uid)->getConnection()->getConfiguration()['ldap_base_users']; + } + + /** + * Get the LDAP base for groups. + * @param string $uid ownCloud user id + * @return string the base for groups + * @throws \Exception if user id was not found in LDAP + */ + public function getLDAPBaseGroups($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + return $this->backend->getLDAPAccess($uid)->getConnection()->getConfiguration()['ldap_base_groups']; + } + + /** + * Clear the cache if a cache is used, otherwise do nothing. + * @param string $uid ownCloud user id + * @throws \Exception if user id was not found in LDAP + */ + public function clearCache($uid) { + if(!$this->backend->userExists($uid)){ + throw new \Exception('User id not found in LDAP'); + } + $this->backend->getLDAPAccess($uid)->getConnection()->clearCache(); + } + + /** + * Check whether a LDAP DN exists + * @param string $dn LDAP DN + * @return bool whether the DN exists + */ + public function dnExists($dn) { + $result = $this->backend->dn2UserName($dn); + return !$result ? false : true; + } + + /** + * Flag record for deletion. + * @param string $uid ownCloud user id + */ + public function flagRecord($uid) { + $this->deletedUsersIndex->markUser($uid); + } + + /** + * Unflag record for deletion. + * @param string $uid ownCloud user id + */ + public function unflagRecord($uid) { + //do nothing + } +} diff --git a/apps/user_ldap/lib/LDAPProviderFactory.php b/apps/user_ldap/lib/LDAPProviderFactory.php new file mode 100644 index 0000000000..6525d14d3f --- /dev/null +++ b/apps/user_ldap/lib/LDAPProviderFactory.php @@ -0,0 +1,57 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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; + +use OCP\LDAP\ILDAPProviderFactory; +use OCP\IServerContainer; +use OCA\User_LDAP\User\DeletedUsersIndex; +use OCA\User_LDAP\Mapping\UserMapping; + +class LDAPProviderFactory implements ILDAPProviderFactory { + /** + * Server container + * + * @var IServerContainer + */ + private $serverContainer; + + /** + * Constructor for the LDAP provider factory + * + * @param IServerContainer $serverContainer server container + */ + public function __construct(IServerContainer $serverContainer) { + $this->serverContainer = $serverContainer; + } + + /** + * creates and returns an instance of the ILDAPProvider + * + * @return OCP\LDAP\ILDAPProvider + */ + public function getLDAPProvider() { + $dbConnection = $this->serverContainer->getDatabaseConnection(); + $userMapping = new UserMapping($dbConnection); + return new LDAPProvider($this->serverContainer, new Helper(), + new DeletedUsersIndex($this->serverContainer->getConfig(), + $dbConnection, $userMapping)); + } +} diff --git a/apps/user_ldap/lib/Proxy.php b/apps/user_ldap/lib/Proxy.php index 07cc1ea0e8..db1c761656 100644 --- a/apps/user_ldap/lib/Proxy.php +++ b/apps/user_ldap/lib/Proxy.php @@ -77,7 +77,7 @@ abstract class Proxy { $userManager = new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, $coreUserManager); $connector = new Connection($this->ldap, $configPrefix); - $access = new Access($connector, $this->ldap, $userManager); + $access = new Access($connector, $this->ldap, $userManager, new Helper()); $access->setUserMapper($userMap); $access->setGroupMapper($groupMap); self::$accesses[$configPrefix] = $access; diff --git a/apps/user_ldap/lib/User_LDAP.php b/apps/user_ldap/lib/User_LDAP.php index a2a65bb840..712cc9601d 100644 --- a/apps/user_ldap/lib/User_LDAP.php +++ b/apps/user_ldap/lib/User_LDAP.php @@ -15,6 +15,7 @@ * @author Robin McCorkell * @author Thomas Müller * @author Tom Needham + * @author Roger Szabo * * @license AGPL-3.0 * @@ -39,7 +40,7 @@ use OCA\User_LDAP\User\OfflineUser; use OCA\User_LDAP\User\User; use OCP\IConfig; -class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface { +class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP { /** @var string[] $homesToKill */ protected $homesToKill = array(); @@ -90,6 +91,16 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn return false; } } + + /** + * returns the username for the given LDAP DN, if available + * + * @param string $dn + * @return string|false with the name to use in ownCloud + */ + public function dn2UserName($dn) { + return $this->access->dn2username($dn); + } /** * returns an LDAP record based on a given login name @@ -462,5 +473,25 @@ class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn public function getBackendName(){ return 'LDAP'; } - + + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid) { + return $this->access; + } + + /** + * Return LDAP connection resource from a cloned connection. + * The cloned connection needs to be closed manually. + * of the current access. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid) { + $connection = clone $this->access->getConnection(); + return $connection->getConnectionResource(); + } } diff --git a/apps/user_ldap/lib/User_Proxy.php b/apps/user_ldap/lib/User_Proxy.php index c86d4f29ec..8537d22a43 100644 --- a/apps/user_ldap/lib/User_Proxy.php +++ b/apps/user_ldap/lib/User_Proxy.php @@ -9,6 +9,7 @@ * @author Morris Jobke * @author Robin McCorkell * @author Thomas Müller + * @author Roger Szabo * * @license AGPL-3.0 * @@ -31,7 +32,7 @@ namespace OCA\User_LDAP; use OCA\User_LDAP\User\User; use OCP\IConfig; -class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface { +class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP { private $backends = array(); private $refBackend = null; @@ -193,6 +194,17 @@ class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface $id = 'LOGINNAME,' . $loginName; return $this->handleRequest($id, 'loginName2UserName', array($loginName)); } + + /** + * returns the username for the given LDAP DN, if available + * + * @param string $dn + * @return string|false with the name to use in ownCloud + */ + public function dn2UserName($dn) { + $id = 'DN,' . $dn; + return $this->handleRequest($id, 'dn2UserName', array($dn)); + } /** * get the user's home directory @@ -273,4 +285,22 @@ class User_Proxy extends Proxy implements \OCP\IUserBackend, \OCP\UserInterface return $users; } + /** + * Return access for LDAP interaction. + * @param string $uid + * @return Access instance of Access for LDAP interaction + */ + public function getLDAPAccess($uid) { + return $this->handleRequest($uid, 'getLDAPAccess', array($uid)); + } + + /** + * Return a new LDAP connection for the specified user. + * The connection needs to be closed manually. + * @param string $uid + * @return resource of the LDAP connection + */ + public function getNewLDAPConnection($uid) { + return $this->handleRequest($uid, 'getNewLDAPConnection', array($uid)); + } } diff --git a/apps/user_ldap/tests/AccessTest.php b/apps/user_ldap/tests/AccessTest.php index f96813ba71..2fddafa214 100644 --- a/apps/user_ldap/tests/AccessTest.php +++ b/apps/user_ldap/tests/AccessTest.php @@ -60,21 +60,22 @@ class AccessTest extends \Test\TestCase { $this->getMock('\OCP\Image'), $this->getMock('\OCP\IDBConnection'), $this->getMock('\OCP\IUserManager'))); + $helper = new \OCA\User_LDAP\Helper(); - return array($lw, $connector, $um); + return array($lw, $connector, $um, $helper); } public function testEscapeFilterPartValidChars() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = 'okay'; $this->assertTrue($input === $access->escapeFilterPart($input)); } public function testEscapeFilterPartEscapeWildcard() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = '*'; $expected = '\\\\*'; @@ -82,8 +83,8 @@ class AccessTest extends \Test\TestCase { } public function testEscapeFilterPartEscapeWildcard2() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $input = 'foo*bar'; $expected = 'foo\\\\*bar'; @@ -92,8 +93,8 @@ class AccessTest extends \Test\TestCase { /** @dataProvider convertSID2StrSuccessData */ public function testConvertSID2StrSuccess(array $sidArray, $sidExpected) { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $sidBinary = implode('', $sidArray); $this->assertSame($sidExpected, $access->convertSID2Str($sidBinary)); @@ -127,8 +128,8 @@ class AccessTest extends \Test\TestCase { } public function testConvertSID2StrInputError() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $sidIllegal = 'foobar'; $sidExpected = ''; @@ -137,8 +138,8 @@ class AccessTest extends \Test\TestCase { } public function testGetDomainDNFromDNSuccess() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $inputDN = 'uid=zaphod,cn=foobar,dc=my,dc=server,dc=com'; $domainDN = 'dc=my,dc=server,dc=com'; @@ -152,8 +153,8 @@ class AccessTest extends \Test\TestCase { } public function testGetDomainDNFromDNError() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $inputDN = 'foobar'; $expected = ''; @@ -187,8 +188,8 @@ class AccessTest extends \Test\TestCase { } public function testStringResemblesDN() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $cases = $this->getResemblesDNInputData(); @@ -208,9 +209,9 @@ class AccessTest extends \Test\TestCase { } public function testStringResemblesDNLDAPmod() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); $lw = new \OCA\User_LDAP\LDAP(); - $access = new Access($con, $lw, $um); + $access = new Access($con, $lw, $um, $helper); if(!function_exists('ldap_explode_dn')) { $this->markTestSkipped('LDAP Module not available'); @@ -224,8 +225,8 @@ class AccessTest extends \Test\TestCase { } public function testCacheUserHome() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $con->expects($this->once()) ->method('writeToCache'); @@ -234,8 +235,8 @@ class AccessTest extends \Test\TestCase { } public function testBatchApplyUserAttributes() { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); - $access = new Access($con, $lw, $um); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); + $access = new Access($con, $lw, $um, $helper); $mapperMock = $this->getMockBuilder('\OCA\User_LDAP\Mapping\UserMapping') ->disableOriginalConstructor() ->getMock(); @@ -294,7 +295,7 @@ class AccessTest extends \Test\TestCase { * @dataProvider dNAttributeProvider */ public function testSanitizeDN($attribute) { - list($lw, $con, $um) = $this->getConnectorAndLdapMock(); + list($lw, $con, $um, $helper) = $this->getConnectorAndLdapMock(); $dnFromServer = 'cn=Mixed Cases,ou=Are Sufficient To,ou=Test,dc=example,dc=org'; @@ -309,7 +310,7 @@ class AccessTest extends \Test\TestCase { $attribute => array('count' => 1, $dnFromServer) ))); - $access = new Access($con, $lw, $um); + $access = new Access($con, $lw, $um, $helper); $values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute); $this->assertSame($values[0], strtolower($dnFromServer)); } diff --git a/apps/user_ldap/tests/Group_LDAPTest.php b/apps/user_ldap/tests/Group_LDAPTest.php index 71120bdb83..83ec2dedf2 100644 --- a/apps/user_ldap/tests/Group_LDAPTest.php +++ b/apps/user_ldap/tests/Group_LDAPTest.php @@ -55,9 +55,10 @@ class Group_LDAPTest extends \Test\TestCase { $um = $this->getMockBuilder('\OCA\User_LDAP\User\Manager') ->disableOriginalConstructor() ->getMock(); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', $accMethods, - array($connector, $lw, $um)); + array($connector, $lw, $um, $helper)); $access->expects($this->any()) ->method('getConnection') diff --git a/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php b/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php index 4ec36617c1..bd56494eac 100644 --- a/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php +++ b/apps/user_ldap/tests/Integration/AbstractIntegrationTest.php @@ -26,6 +26,7 @@ namespace OCA\User_LDAP\Tests\Integration; use OCA\User_LDAP\Access; use OCA\User_LDAP\Connection; use OCA\User_LDAP\LDAP; +use OCA\User_LDAP\Helper; use OCA\User_LDAP\User\Manager; abstract class AbstractIntegrationTest { @@ -40,6 +41,9 @@ abstract class AbstractIntegrationTest { /** @var Manager */ protected $userManager; + + /** @var Helper */ + protected $helper; /** @var string */ protected $base; @@ -65,6 +69,7 @@ abstract class AbstractIntegrationTest { $this->initLDAPWrapper(); $this->initConnection(); $this->initUserManager(); + $this->initHelper(); $this->initAccess(); } @@ -103,12 +108,19 @@ abstract class AbstractIntegrationTest { protected function initUserManager() { $this->userManager = new FakeManager(); } + + /** + * initializes the test Helper + */ + protected function initHelper() { + $this->helper = new Helper(); + } /** * initializes the Access test instance */ protected function initAccess() { - $this->access = new Access($this->connection, $this->ldap, $this->userManager); + $this->access = new Access($this->connection, $this->ldap, $this->userManager, $this->helper); } /** diff --git a/apps/user_ldap/tests/Jobs/CleanUpTest.php b/apps/user_ldap/tests/Jobs/CleanUpTest.php index 45e7998da0..4785a227d2 100644 --- a/apps/user_ldap/tests/Jobs/CleanUpTest.php +++ b/apps/user_ldap/tests/Jobs/CleanUpTest.php @@ -37,7 +37,7 @@ class CleanUpTest extends \Test\TestCase { ->getMock(); $mocks['ocConfig'] = $this->getMock('\OCP\IConfig'); $mocks['db'] = $this->getMock('\OCP\IDBConnection'); - $mocks['helper'] = $this->getMock('\OCA\User_LDAP\Helper'); + $mocks['helper'] = new \OCA\User_LDAP\Helper(); return $mocks; } diff --git a/apps/user_ldap/tests/LDAPProviderTest.php b/apps/user_ldap/tests/LDAPProviderTest.php new file mode 100644 index 0000000000..071cc09fa6 --- /dev/null +++ b/apps/user_ldap/tests/LDAPProviderTest.php @@ -0,0 +1,337 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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; + +use OCP\IServerContainer; +use OCA\User_LDAP\IUserLDAP; + +/** + * Class LDAPProviderTest + * + * @group DB + * + * @package OCA\User_LDAP\Tests + */ +class LDAPProviderTest extends \Test\TestCase { + + protected function setUp() { + parent::setUp(); + } + + private function getServerMock(IUserLDAP $backend) { + $server = $this->getMockBuilder('OC\Server') + ->setMethods(['getUserManager', 'getBackends']) + ->setConstructorArgs(['', new \OC\Config(\OC::$configDir)]) + ->getMock(); + $server->expects($this->at(1)) + ->method('getBackends') + ->willReturn([$backend]); + $server->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + return $server; + } + + private function getLDAPProvider(IServerContainer $serverContainer) { + $factory = new \OCA\User_LDAP\LDAPProviderFactory($serverContainer); + return $factory->getLDAPProvider(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetUserDNUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getUserDN('nonexisting_user'); + } + + public function testGetUserDN() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'username2dn']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(2)) + ->method('username2dn') + ->willReturn('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org', + $ldapProvider->getUserDN('existing_user')); + } + + public function testGetUserName() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['dn2UserName']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('dn2UserName') + ->willReturn('existing_user'); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('existing_user', + $ldapProvider->getUserName('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testDNasBaseParameter() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $helper = new \OCA\User_LDAP\Helper(); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals( + $helper->DNasBaseParameter('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'), + $ldapProvider->DNasBaseParameter('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testSanitizeDN() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $helper = new \OCA\User_LDAP\Helper(); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals( + $helper->sanitizeDN('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org'), + $ldapProvider->sanitizeDN('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPConnectionUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPConnection('nonexisting_user'); + } + + public function testGetLDAPConnection() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getNewLDAPConnection']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->any()) + ->method('getNewLDAPConnection') + ->willReturn(true); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertTrue($ldapProvider->getLDAPConnection('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPBaseUsersUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPBaseUsers('nonexisting_user'); + } + + public function testGetLDAPBaseUsers() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'getConfiguration']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('getConfiguration') + ->willReturn(array('ldap_base_users'=>'ou=users,dc=example,dc=org')); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('ou=users,dc=example,dc=org', $ldapProvider->getLDAPBaseUsers('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testGetLDAPBaseGroupsUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->getLDAPBaseGroups('nonexisting_user'); + } + + public function testGetLDAPBaseGroups() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'getConfiguration']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('getConfiguration') + ->willReturn(array('ldap_base_groups'=>'ou=groups,dc=example,dc=org')); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertEquals('ou=groups,dc=example,dc=org', $ldapProvider->getLDAPBaseGroups('existing_user')); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage User id not found in LDAP + */ + public function testClearCacheUserIDNotFound() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any())->method('userExists')->willReturn(false); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->clearCache('nonexisting_user'); + } + + public function testClearCache() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['userExists', 'getLDAPAccess', 'getConnection', 'clearCache']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->at(0)) + ->method('userExists') + ->willReturn(true); + $backend->expects($this->at(3)) + ->method('clearCache') + ->willReturn(true); + $backend->expects($this->any()) + ->method($this->anything()) + ->willReturnSelf(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->clearCache('existing_user'); + $this->assertTrue(TRUE); + } + + public function testDnExists() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods(['dn2UserName']) + ->disableOriginalConstructor() + ->getMock(); + $backend->expects($this->any()) + ->method('dn2UserName') + ->willReturn('existing_user'); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $this->assertTrue($ldapProvider->dnExists('cn=existing_user,ou=Are Sufficient To,ou=Test,dc=example,dc=org')); + } + + public function testFlagRecord() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->flagRecord('existing_user'); + $this->assertTrue(TRUE); + } + + public function testUnflagRecord() { + $backend = $this->getMockBuilder('OCA\User_LDAP\User_LDAP') + ->setMethods([]) + ->disableOriginalConstructor() + ->getMock(); + + $server = $this->getServerMock($backend); + + $ldapProvider = $this->getLDAPProvider($server); + $ldapProvider->unflagRecord('existing_user'); + $this->assertTrue(TRUE); + } +} diff --git a/apps/user_ldap/tests/User/UserTest.php b/apps/user_ldap/tests/User/UserTest.php index cf1514009d..d9e43dee04 100644 --- a/apps/user_ldap/tests/User/UserTest.php +++ b/apps/user_ldap/tests/User/UserTest.php @@ -71,8 +71,9 @@ class UserTest extends \Test\TestCase { $umMethods, array($cfMock, $fsMock, $logMock, $avaMgr, $im, $dbc, $userMgr)); $connector = $this->getMock('\OCA\User_LDAP\Connection', $conMethods, array($lw, null, null)); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', - $accMethods, array($connector, $lw, $um)); + $accMethods, array($connector, $lw, $um, $helper)); return array($access, $connector); } diff --git a/apps/user_ldap/tests/User_LDAPTest.php b/apps/user_ldap/tests/User_LDAPTest.php index ae86e3cf3a..47e789142e 100644 --- a/apps/user_ldap/tests/User_LDAPTest.php +++ b/apps/user_ldap/tests/User_LDAPTest.php @@ -93,9 +93,11 @@ class User_LDAPTest extends \Test\TestCase { ->method('getDeletedUser') ->will($this->returnValue($offlineUser)); + $helper = new \OCA\User_LDAP\Helper(); + $access = $this->getMock('\OCA\User_LDAP\Access', $accMethods, - array($connector, $lw, $um)); + array($connector, $lw, $um, $helper)); $um->setLdapAccess($access); diff --git a/apps/user_ldap/tests/WizardTest.php b/apps/user_ldap/tests/WizardTest.php index cc110c6ee0..e82cbcfc82 100644 --- a/apps/user_ldap/tests/WizardTest.php +++ b/apps/user_ldap/tests/WizardTest.php @@ -69,8 +69,9 @@ class WizardTest extends \Test\TestCase { $um = $this->getMockBuilder('\OCA\User_LDAP\User\Manager') ->disableOriginalConstructor() ->getMock(); + $helper = new \OCA\User_LDAP\Helper(); $access = $this->getMock('\OCA\User_LDAP\Access', - $accMethods, array($connector, $lw, $um)); + $accMethods, array($connector, $lw, $um, $helper)); return array(new Wizard($conf, $lw, $access), $conf, $lw, $access); } diff --git a/lib/private/Server.php b/lib/private/Server.php index de2970d9bf..d7352613f2 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -21,6 +21,7 @@ * @author Thomas Müller * @author Thomas Tanghus * @author Vincent Petry + * @author Roger Szabo * * @license AGPL-3.0 * @@ -583,6 +584,16 @@ class Server extends ServerContainer implements IServerContainer { $this->getLogger() ); }); + $this->registerService('LDAPProvider', function(Server $c) { + $config = $c->getConfig(); + $factoryClass = $config->getSystemValue('ldapProviderFactory', null); + if(is_null($factoryClass)) { + throw new \Exception('ldapProviderFactory not set'); + } + /** @var \OCP\LDAP\ILDAPProviderFactory $factory */ + $factory = new $factoryClass($this); + return $factory->getLDAPProvider(); + }); $this->registerService('LockingProvider', function (Server $c) { $ini = $c->getIniWrapper(); $config = $c->getConfig(); @@ -1398,4 +1409,12 @@ class Server extends ServerContainer implements IServerContainer { return $this->query('ShareManager'); } + /** + * Returns the LDAP Provider + * + * @return \OCP\LDAP\ILDAPProvider + */ + public function getLDAPProvider() { + return $this->query('LDAPProvider'); + } } diff --git a/lib/public/LDAP/IDeletionFlagSupport.php b/lib/public/LDAP/IDeletionFlagSupport.php new file mode 100644 index 0000000000..ca272d7aa7 --- /dev/null +++ b/lib/public/LDAP/IDeletionFlagSupport.php @@ -0,0 +1,44 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 OCP\LDAP; + +/** + * Interface IDeletionFlagSupport + * + * @package OCP\LDAP + * @since 9.2.0 + */ +interface IDeletionFlagSupport { + /** + * Flag record for deletion. + * @param string $uid ownCloud user id + * @since 9.2.0 + */ + public function flagRecord($uid); + + /** + * Unflag record for deletion. + * @param string $uid ownCloud user id + * @since 9.2.0 + */ + public function unflagRecord($uid); +} diff --git a/lib/public/LDAP/ILDAPProvider.php b/lib/public/LDAP/ILDAPProvider.php new file mode 100644 index 0000000000..44f7d88c94 --- /dev/null +++ b/lib/public/LDAP/ILDAPProvider.php @@ -0,0 +1,104 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 OCP\LDAP; + +/** + * Interface ILDAPProvider + * + * @package OCP\LDAP + * @since 9.2.0 + */ +interface ILDAPProvider { + /** + * Translate an ownCloud username to LDAP DN. + * @param string $uid ownCloud user id + * @return string + * @since 9.2.0 + */ + public function getUserDN($uid); + + /** + * Translate a LDAP DN to an ownCloud user name. + * @param string $dn LDAP DN + * @return string with the ownCloud user name + * @throws \Exception if translation was unsuccessful + * @since 9.2.0 + */ + public function getUserName($dn); + + /** + * Convert a stored DN so it can be used as base parameter for LDAP queries. + * @param string $dn the DN + * @return string + * @since 9.2.0 + */ + public function DNasBaseParameter($dn); + + /** + * Sanitize a DN received from the LDAP server. + * @param array $dn the DN in question + * @return array the sanitized DN + * @since 9.2.0 + */ + public function sanitizeDN($dn); + + /** + * Return a new LDAP connection resource for the specified user. + * @param string $uid ownCloud user id + * @return resource of the LDAP connection + * @since 9.2.0 + */ + public function getLDAPConnection($uid); + + /** + * Get the LDAP base for users. + * @param string $uid ownCloud user id + * @return string the base for users + * @throws \Exception if user id was not found in LDAP + * @since 9.2.0 + */ + public function getLDAPBaseUsers($uid); + + /** + * Get the LDAP base for groups. + * @param string $uid ownCloud user id + * @return string the base for groups + * @throws \Exception if user id was not found in LDAP + * @since 9.2.0 + */ + public function getLDAPBaseGroups($uid); + + /** + * Check whether a LDAP DN exists + * @param string $dn LDAP DN + * @return bool whether the DN exists + * @since 9.2.0 + */ + public function dnExists($dn); + + /** + * Clear the cache if a cache is used, otherwise do nothing. + * @param string $uid ownCloud user id + * @since 9.2.0 + */ + public function clearCache($uid); +} diff --git a/lib/public/LDAP/ILDAPProviderFactory.php b/lib/public/LDAP/ILDAPProviderFactory.php new file mode 100644 index 0000000000..3e1242e33d --- /dev/null +++ b/lib/public/LDAP/ILDAPProviderFactory.php @@ -0,0 +1,51 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 OCP\LDAP; + +use OCP\IServerContainer; + +/** + * Interface ILDAPProviderFactory + * + * This class is responsible for instantiating and returning an ILDAPProvider + * instance. + * + * @package OCP\LDAP + * @since 9.2.0 + */ +interface ILDAPProviderFactory { + + /** + * Constructor for the LDAP provider factory + * + * @param IServerContainer $serverContainer server container + * @since 9.2.0 + */ + public function __construct(IServerContainer $serverContainer); + + /** + * creates and returns an instance of the ILDAPProvider + * + * @return ILDAPProvider + * @since 9.2.0 + */ + public function getLDAPProvider(); +}