From d5c111a984278a097025d267f3461c01983ca0c2 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 23 Aug 2012 18:29:43 +0200 Subject: [PATCH] LDAP: identify (map) users with their directory UUID. Fixes the issue, that usernames for owncloud will change, when the DN changes (which happens rarely, but it happens). --- apps/user_ldap/appinfo/database.xml | 16 ++++++ apps/user_ldap/appinfo/update.php | 42 ++++++++------- apps/user_ldap/appinfo/version | 2 +- apps/user_ldap/lib/access.php | 81 +++++++++++++++++++++++++++-- apps/user_ldap/lib/connection.php | 25 +++++++++ 5 files changed, 140 insertions(+), 26 deletions(-) diff --git a/apps/user_ldap/appinfo/database.xml b/apps/user_ldap/appinfo/database.xml index 3bbd2b09a4..a785bbf422 100644 --- a/apps/user_ldap/appinfo/database.xml +++ b/apps/user_ldap/appinfo/database.xml @@ -28,6 +28,14 @@ + + directory_uuid + text + true + 255 + + + ldap_dn_users true @@ -71,6 +79,14 @@ + + directory_uuid + text + true + 255 + + + ldap_dn_groups true diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index badceb378d..9cf1814cf6 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -2,6 +2,11 @@ //from version 0.1 to 0.2 +//ATTENTION +//Upgrade from ownCloud 3 (LDAP backend 0.1) to ownCloud 4.5 (LDAP backend 0.3) is not supported!! +//You must do upgrade to ownCloud 4.0 first! +//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad efffects. + //settings $pw = OCP\Config::getAppValue('user_ldap', 'ldap_password'); if(!is_null($pw)) { @@ -12,33 +17,25 @@ if(!is_null($pw)) { //detect if we can switch on naming guidelines. We won't do it on conflicts. //it's a bit spaghetti, but hey. -$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'doCheck'); -if($state == 'doCheck'){ - $sqlCleanMap = 'DELETE FROM *PREFIX*ldap_user_mapping'; - - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); - $LDAP_USER = new OC_USER_LDAP(); - $users_old = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); +$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'unset'); +if($state == 'unset'){ OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); - OC_LDAP::init(true); - $users_new = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); - if($users_old !== $users_new) { - //we don't need to check Groups, because they were not supported in 3' - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); - } } +// ### SUPPORTED upgrade path starts here ### -//from version 0.2 to 0.2.1 +//from version 0.2 to 0.3 (0.2.0.x dev version) $objects = array('user', 'group'); +$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); +$userBE = new \OCA\user_ldap\USER_LDAP(); +$userBE->setConnector($connector); +$groupBE = new \OCA\user_ldap\GROUP_LDAP(); +$groupBE->setConnector($connector); + foreach($objects as $object) { $fetchDNSql = 'SELECT ldap_dn from *PREFIX*ldap_'.$object.'_mapping'; - $updateSql = 'UPDATE *PREFIX*ldap_'.$object.'_mapping SET ldap_DN = ? WHERE ldap_dn = ?'; + $updateSql = 'UPDATE *PREFIX*ldap_'.$object.'_mapping SET ldap_DN = ?, directory_uuid = ? WHERE ldap_dn = ?'; $query = OCP\DB::prepare($fetchDNSql); $res = $query->execute(); @@ -46,6 +43,11 @@ foreach($objects as $object) { $updateQuery = OCP\DB::prepare($updateSql); foreach($DNs as $dn) { $newDN = mb_strtolower($dn['ldap_dn'], 'UTF-8'); - $updateQuery->execute(array($newDN, $dn['ldap_dn'])); + if($object == 'user') { + $uuid = $userBE->getUUID($newDN); + } else { + $uuid = $groupBE->getUUID($newDN); + } + $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn'])); } } diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index 1683a26615..e689e4949e 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.2.0.8 \ No newline at end of file +0.2.0.26 \ No newline at end of file diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 4e7215525d..be9aa21c3d 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -50,11 +50,12 @@ abstract class Access { $cr = $this->connection->getConnectionResource(); if(!is_resource($cr)) { //LDAP not available + \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG); return false; } $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr)); if(!is_resource($rr)) { - \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', 'readAttribute '.$attr.' failed for DN '.$dn, \OCP\Util::DEBUG); //in case an error occurs , e.g. object does not exist return false; } @@ -70,6 +71,7 @@ abstract class Access { } return $values; } + \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG); return false; } @@ -226,11 +228,32 @@ abstract class Access { WHERE ldap_dn = ? '); + //let's try to retrieve the ownCloud name from the mappings table $component = $query->execute(array($dn))->fetchOne(); if($component) { return $component; } + //second try: get the UUID and check if it is known. Then, update the DN and return the name. + $uuid = $this->getUUID($dn); + if($uuid) { + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE directory_uuid = ? + '); + $component = $query->execute(array($uuid))->fetchOne(); + if($component) { + $query = \OCP\DB::prepare(' + UPDATE '.$table.' + SET ldap_dn = ? + WHERE directory_uuid = ? + '); + $query->execute(array($dn, $uuid)); + return $component; + } + } + if(is_null($ldapname)) { $ldapname = $this->readAttribute($dn, $nameAttribute); if(!isset($ldapname[0]) && empty($ldapname[0])) { @@ -389,17 +412,18 @@ abstract class Access { } $insert = \OCP\DB::prepare(' - INSERT INTO '.$table.' (ldap_dn, owncloud_name) - SELECT ?,? + INSERT INTO '.$table.' (ldap_dn, owncloud_name, directory_uuid) + SELECT ?,?,? '.$sqlAdjustment.' WHERE NOT EXISTS ( SELECT 1 FROM '.$table.' WHERE ldap_dn = ? - OR owncloud_name = ? ) + OR owncloud_name = ?) '); - $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); + //feed the DB + $res = $insert->execute(array($dn, $ocname, $this->getUUID($dn), $dn, $ocname)); if(\OCP\DB::isError($res)) { return false; @@ -602,4 +626,51 @@ abstract class Access { } return $testConnection->bind(); } + + /** + * @brief auto-detects the directory's UUID attribute + * @param $dn a known DN used to check against + * @param $force the detection should be run, even if it is not set to auto + * @returns true on success, false otherwise + */ + private function detectUuidAttribute($dn, $force = false) { + if(($this->connection->ldapUuidAttribute != 'auto') && !$force) { + return true; + } + + //for now, supported (known) attributes are entryUUID, nsuniqueid, objectGUID + $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid'); + + foreach($testAttributes as $attribute) { + \OCP\Util::writeLog('user_ldap', 'Testing '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + + $value = $this->readAttribute($dn, $attribute); + if(is_array($value) && isset($value[0]) && !empty($value[0])) { + \OCP\Util::writeLog('user_ldap', 'Setting '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + $this->connection->ldapUuidAttribute = $attribute; + return true; + } + \OCP\Util::writeLog('user_ldap', 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value,true), \OCP\Util::DEBUG); + } + + return false; + } + + public function getUUID($dn) { + if($this->detectUuidAttribute($dn)) { + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + if(!is_array($uuid) && $this->connection->ldapOverrideUuidAttribute) { + $this->detectUuidAttribute($dn, true); + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + } + if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) { + $uuid = $uuid[0]; + } else { + $uuid = false; + } + } else { + $uuid = false; + } + return $uuid; + } } \ No newline at end of file diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 9bb012e910..dc160a1642 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -53,6 +53,8 @@ class Connection { 'ldapQuotaDefault' => null, 'ldapEmailAttribute' => null, 'ldapCacheTTL' => null, + 'ldapUuidAttribute' => null, + 'ldapOverrideUuidAttribute' => null, ); public function __construct($configID = 'user_ldap') { @@ -74,6 +76,22 @@ class Connection { } } + public function __set($name, $value) { + $changed = false; + //omly few options are writable + if($name == 'ldapUuidAttribute') { + \OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG); + $this->config[$name] = $value; + if(!empty($this->configID)) { + \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', $value); + } + $changed = true; + } + if($changed) { + $this->validateConfiguration(); + } + } + /** * @brief initializes the LDAP backend * @param $force read the config settings no matter what @@ -180,6 +198,8 @@ class Connection { $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); $this->config['ldapCacheTTL'] = \OCP\Config::getAppValue($this->configID, 'ldap_cache_ttl', 10*60); + $this->config['ldapUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + $this->config['ldapOverrideUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_override_uuid_attribute', 0); $this->configured = $this->validateConfiguration(); } @@ -236,6 +256,11 @@ class Connection { if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); } + if(!in_array($this->config['ldapUuidAttribute'], array('auto','entryuuid', 'nsuniqueid', 'objectguid'))) { + \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO); + } + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. $configurationOK = true;