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

remotes/origin/stable45
Arthur Schiwon 13 years ago
parent c90c358f0d
commit d5c111a984
  1. 16
      apps/user_ldap/appinfo/database.xml
  2. 42
      apps/user_ldap/appinfo/update.php
  3. 2
      apps/user_ldap/appinfo/version
  4. 81
      apps/user_ldap/lib/access.php
  5. 25
      apps/user_ldap/lib/connection.php

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

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

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

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

Loading…
Cancel
Save