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).

This commit is contained in:
Arthur Schiwon 2012-08-23 18:29:43 +02:00
parent c90c358f0d
commit d5c111a984
5 changed files with 140 additions and 26 deletions

View File

@ -28,6 +28,14 @@
<default></default>
</field>
<field>
<name>directory_uuid</name>
<type>text</type>
<notnull>true</notnull>
<length>255</length>
<default></default>
</field>
<index>
<name>ldap_dn_users</name>
<unique>true</unique>
@ -71,6 +79,14 @@
<default></default>
</field>
<field>
<name>directory_uuid</name>
<type>text</type>
<notnull>true</notnull>
<length>255</length>
<default></default>
</field>
<index>
<name>ldap_dn_groups</name>
<unique>true</unique>

View File

@ -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']));
}
}

View File

@ -1 +1 @@
0.2.0.8
0.2.0.26

View File

@ -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;
}
}

View File

@ -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;