Merge pull request #1087 from owncloud/ldap_multiple_dns
LDAP: ability to configure more than one base DN
This commit is contained in:
commit
bb9cc227c2
|
@ -2,9 +2,11 @@
|
|||
width: 20%;
|
||||
max-width: 200px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
#ldap fieldset input {
|
||||
#ldap fieldset input, #ldap fieldset textarea {
|
||||
width: 70%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,15 @@ abstract class Access {
|
|||
* @return 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);
|
||||
|
||||
|
@ -212,9 +221,13 @@ abstract class Access {
|
|||
* returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
|
||||
*/
|
||||
public function dn2groupname($dn, $ldapname = null) {
|
||||
if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseGroups), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseGroups), 'UTF-8'))) {
|
||||
//To avoid bypassing the base DN settings under certain circumstances
|
||||
//with the group support, check whether the provided DN matches one of
|
||||
//the given Bases
|
||||
if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->dn2ocname($dn, $ldapname, false);
|
||||
}
|
||||
|
||||
|
@ -227,9 +240,13 @@ abstract class Access {
|
|||
* returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
|
||||
*/
|
||||
public function dn2username($dn, $ldapname = null) {
|
||||
if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseUsers), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseUsers), 'UTF-8'))) {
|
||||
//To avoid bypassing the base DN settings under certain circumstances
|
||||
//with the group support, check whether the provided DN matches one of
|
||||
//the given Bases
|
||||
if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseUsers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->dn2ocname($dn, $ldapname, true);
|
||||
}
|
||||
|
||||
|
@ -521,7 +538,7 @@ abstract class Access {
|
|||
/**
|
||||
* @brief executes an LDAP search
|
||||
* @param $filter the LDAP filter for the search
|
||||
* @param $base the LDAP subtree that shall be searched
|
||||
* @param $base an array containing the LDAP subtree(s) that shall be searched
|
||||
* @param $attr optional, when a certain attribute shall be filtered out
|
||||
* @returns array with the search result
|
||||
*
|
||||
|
@ -544,18 +561,28 @@ abstract class Access {
|
|||
//check wether paged search should be attempted
|
||||
$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, $limit, $offset);
|
||||
|
||||
$sr = ldap_search($link_resource, $base, $filter, $attr);
|
||||
if(!$sr) {
|
||||
$linkResources = array_pad(array(), count($base), $link_resource);
|
||||
$sr = ldap_search($linkResources, $base, $filter, $attr);
|
||||
$error = ldap_errno($link_resource);
|
||||
if(!is_array($sr) || $error > 0) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('user_ldap', 'Attempt for Paging? '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
|
||||
return array();
|
||||
}
|
||||
$findings = ldap_get_entries($link_resource, $sr );
|
||||
$findings = array();
|
||||
foreach($sr as $key => $res) {
|
||||
$findings = array_merge($findings, ldap_get_entries($link_resource, $res ));
|
||||
}
|
||||
if($pagedSearchOK) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO);
|
||||
ldap_control_paged_result_response($link_resource, $sr, $cookie);
|
||||
\OCP\Util::writeLog('user_ldap', 'Set paged search cookie '.$cookie, \OCP\Util::INFO);
|
||||
$this->setPagedResultCookie($filter, $limit, $offset, $cookie);
|
||||
foreach($sr as $key => $res) {
|
||||
$cookie = null;
|
||||
if(ldap_control_paged_result_response($link_resource, $res, $cookie)) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Set paged search cookie', \OCP\Util::INFO);
|
||||
$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
|
||||
}
|
||||
}
|
||||
|
||||
//browsing through prior pages to get the cookie for the new one
|
||||
if($skipHandling) {
|
||||
return;
|
||||
|
@ -565,7 +592,9 @@ abstract class Access {
|
|||
$this->pagedSearchedSuccessful = true;
|
||||
}
|
||||
} else {
|
||||
\OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO);
|
||||
if(!is_null($limit)) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Paged search failed :(', \OCP\Util::INFO);
|
||||
}
|
||||
}
|
||||
|
||||
// if we're here, probably no connection resource is returned.
|
||||
|
@ -791,20 +820,41 @@ abstract class Access {
|
|||
return str_replace('\\5c', '\\', $dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief checks if the given DN is part of the given base DN(s)
|
||||
* @param $dn the DN
|
||||
* @param $bases array containing the allowed base DN or DNs
|
||||
* @returns Boolean
|
||||
*/
|
||||
private function isDNPartOfBase($dn, $bases) {
|
||||
$bases = $this->sanitizeDN($bases);
|
||||
foreach($bases as $base) {
|
||||
$belongsToBase = true;
|
||||
if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base))) {
|
||||
$belongsToBase = false;
|
||||
}
|
||||
if($belongsToBase) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $belongsToBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get a cookie for the next LDAP paged search
|
||||
* @param $base a string with the base DN for the search
|
||||
* @param $filter the search filter to identify the correct search
|
||||
* @param $limit the limit (or 'pageSize'), to identify the correct search well
|
||||
* @param $offset the offset for the new search to identify the correct search really good
|
||||
* @returns string containing the key or empty if none is cached
|
||||
*/
|
||||
private function getPagedResultCookie($filter, $limit, $offset) {
|
||||
private function getPagedResultCookie($base, $filter, $limit, $offset) {
|
||||
if($offset == 0) {
|
||||
return '';
|
||||
}
|
||||
$offset -= $limit;
|
||||
//we work with cache here
|
||||
$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
|
||||
$cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset;
|
||||
$cookie = $this->connection->getFromCache($cachekey);
|
||||
if(is_null($cookie)) {
|
||||
$cookie = '';
|
||||
|
@ -814,15 +864,16 @@ abstract class Access {
|
|||
|
||||
/**
|
||||
* @brief set a cookie for LDAP paged search run
|
||||
* @param $base a string with the base DN for the search
|
||||
* @param $filter the search filter to identify the correct search
|
||||
* @param $limit the limit (or 'pageSize'), to identify the correct search well
|
||||
* @param $offset the offset for the run search to identify the correct search really good
|
||||
* @param $cookie string containing the cookie returned by ldap_control_paged_result_response
|
||||
* @return void
|
||||
*/
|
||||
private function setPagedResultCookie($filter, $limit, $offset) {
|
||||
private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
|
||||
if(!empty($cookie)) {
|
||||
$cachekey = 'lc' . dechex(crc32($filter)) . '-' . $limit . '-' . $offset;
|
||||
$cachekey = 'lc' . dechex(crc32($base)) . '-' . dechex(crc32($filter)) . '-' .$limit . '-' . $offset;
|
||||
$cookie = $this->connection->writeToCache($cachekey, $cookie);
|
||||
}
|
||||
}
|
||||
|
@ -841,40 +892,47 @@ abstract class Access {
|
|||
/**
|
||||
* @brief prepares a paged search, if possible
|
||||
* @param $filter the LDAP filter for the search
|
||||
* @param $base the LDAP subtree that shall be searched
|
||||
* @param $bases an array containing the LDAP subtree(s) that shall be searched
|
||||
* @param $attr optional, when a certain attribute shall be filtered outside
|
||||
* @param $limit
|
||||
* @param $offset
|
||||
*
|
||||
*/
|
||||
private function initPagedSearch($filter, $base, $attr, $limit, $offset) {
|
||||
private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
|
||||
$pagedSearchOK = false;
|
||||
if($this->connection->hasPagedResultSupport && !is_null($limit)) {
|
||||
$offset = intval($offset); //can be null
|
||||
\OCP\Util::writeLog('user_ldap', 'initializing paged search for Filter'.$filter.' base '.$base.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, \OCP\Util::DEBUG);
|
||||
\OCP\Util::writeLog('user_ldap', 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true).' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, \OCP\Util::INFO);
|
||||
//get the cookie from the search for the previous search, required by LDAP
|
||||
$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
|
||||
if(empty($cookie) && ($offset > 0)) {
|
||||
//no cookie known, although the offset is not 0. Maybe cache run out. We need to start all over *sigh* (btw, Dear Reader, did you need LDAP paged searching was designed by MSFT?)
|
||||
$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
|
||||
//a bit recursive, $offset of 0 is the exit
|
||||
\OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
|
||||
$this->search($filter, $base, $attr, $limit, $reOffset, true);
|
||||
$cookie = $this->getPagedResultCookie($filter, $limit, $offset);
|
||||
//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
|
||||
//TODO: remember this, probably does not change in the next request...
|
||||
if(empty($cookie)) {
|
||||
$cookie = null;
|
||||
foreach($bases as $base) {
|
||||
|
||||
$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
|
||||
if(empty($cookie) && ($offset > 0)) {
|
||||
//no cookie known, although the offset is not 0. Maybe cache run out. We need to start all over *sigh* (btw, Dear Reader, did you need LDAP paged searching was designed by MSFT?)
|
||||
$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
|
||||
//a bit recursive, $offset of 0 is the exit
|
||||
\OCP\Util::writeLog('user_ldap', 'Looking for cookie L/O '.$limit.'/'.$reOffset, \OCP\Util::INFO);
|
||||
$this->search($filter, $base, $attr, $limit, $reOffset, true);
|
||||
$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
|
||||
//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
|
||||
//TODO: remember this, probably does not change in the next request...
|
||||
if(empty($cookie)) {
|
||||
$cookie = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!is_null($cookie)) {
|
||||
if($offset > 0) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO);
|
||||
if(!is_null($cookie)) {
|
||||
if($offset > 0) {
|
||||
\OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO);
|
||||
}
|
||||
$pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(), $limit, false, $cookie);
|
||||
if(!$pagedSearchOK) {
|
||||
return false;
|
||||
}
|
||||
\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::INFO);
|
||||
} else {
|
||||
\OCP\Util::writeLog('user_ldap', 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, \OCP\Util::INFO);
|
||||
}
|
||||
$pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(), $limit, false, $cookie);
|
||||
\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::INFO);
|
||||
} else {
|
||||
\OCP\Util::writeLog('user_ldap', 'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset, \OCP\Util::INFO);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,9 +187,9 @@ class Connection {
|
|||
$this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389);
|
||||
$this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn', '');
|
||||
$this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password', ''));
|
||||
$this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', '');
|
||||
$this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users', $this->config['ldapBase']);
|
||||
$this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']);
|
||||
$this->config['ldapBase'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base', ''));
|
||||
$this->config['ldapBaseUsers'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base_users', $this->config['ldapBase']));
|
||||
$this->config['ldapBaseGroups'] = preg_split('/\r\n|\r|\n/', \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']));
|
||||
$this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls', 0);
|
||||
$this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0);
|
||||
$this->config['turnOffCertCheck'] = \OCP\Config::getAppValue($this->configID, 'ldap_turn_off_cert_check', 0);
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
echo '<p class="ldapwarning">'.$l->t('<b>Warning:</b> Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behaviour. Please ask your system administrator to disable one of them.').'</p>';
|
||||
}
|
||||
if(!function_exists('ldap_connect')) {
|
||||
echo '<p class="ldapwarning">'.$l->t('<b>Warning:</b> The PHP LDAP module needs is not installed, the backend will not work. Please ask your system administrator to install it.').'</p>';
|
||||
echo '<p class="ldapwarning">'.$l->t('<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.').'</p>';
|
||||
}
|
||||
?>
|
||||
<fieldset id="ldapSettings-1">
|
||||
<p><label for="ldap_host"><?php echo $l->t('Host');?></label><input type="text" id="ldap_host" name="ldap_host" value="<?php echo $_['ldap_host']; ?>" title="<?php echo $l->t('You can omit the protocol, except you require SSL. Then start with ldaps://');?>"></p>
|
||||
<p><label for="ldap_base"><?php echo $l->t('Base DN');?></label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" title="<?php echo $l->t('You can specify Base DN for users and groups in the Advanced tab');?>" /></p>
|
||||
<p><label for="ldap_base"><?php echo $l->t('Base DN');?></label><textarea id="ldap_base" name="ldap_base" placeholder="<?php echo $l->t('One Base DN per line');?>" title="<?php echo $l->t('You can specify Base DN for users and groups in the Advanced tab');?>"><?php echo $_['ldap_base']; ?></textarea></p>
|
||||
<p><label for="ldap_dn"><?php echo $l->t('User DN');?></label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" title="<?php echo $l->t('The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.');?>" /></p>
|
||||
<p><label for="ldap_agent_password"><?php echo $l->t('Password');?></label><input type="password" id="ldap_agent_password" name="ldap_agent_password" value="<?php echo $_['ldap_agent_password']; ?>" title="<?php echo $l->t('For anonymous access, leave DN and Password empty.');?>" /></p>
|
||||
<p><label for="ldap_login_filter"><?php echo $l->t('User Login Filter');?></label><input type="text" id="ldap_login_filter" name="ldap_login_filter" value="<?php echo $_['ldap_login_filter']; ?>" title="<?php echo $l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action.');?>" /><br /><small><?php echo $l->t('use %%uid placeholder, e.g. "uid=%%uid"');?></small></p>
|
||||
|
@ -22,8 +22,8 @@
|
|||
</fieldset>
|
||||
<fieldset id="ldapSettings-2">
|
||||
<p><label for="ldap_port"><?php echo $l->t('Port');?></label><input type="text" id="ldap_port" name="ldap_port" value="<?php echo $_['ldap_port']; ?>" /></p>
|
||||
<p><label for="ldap_base_users"><?php echo $l->t('Base User Tree');?></label><input type="text" id="ldap_base_users" name="ldap_base_users" value="<?php echo $_['ldap_base_users']; ?>" /></p>
|
||||
<p><label for="ldap_base_groups"><?php echo $l->t('Base Group Tree');?></label><input type="text" id="ldap_base_groups" name="ldap_base_groups" value="<?php echo $_['ldap_base_groups']; ?>" /></p>
|
||||
<p><label for="ldap_base_users"><?php echo $l->t('Base User Tree');?></label><textarea id="ldap_base_users" name="ldap_base_users" placeholder="<?php echo $l->t('One User Base DN per line');?>" title="<?php echo $l->t('Base User Tree');?>"><?php echo $_['ldap_base_users']; ?></textarea></p>
|
||||
<p><label for="ldap_base_groups"><?php echo $l->t('Base Group Tree');?></label><textarea id="ldap_base_groups" name="ldap_base_groups" placeholder="<?php echo $l->t('One Group Base DN per line');?>" title="<?php echo $l->t('Base Group Tree');?>"><?php echo $_['ldap_base_groups']; ?></textarea></p>
|
||||
<p><label for="ldap_group_member_assoc_attribute"><?php echo $l->t('Group-Member association');?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute"><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'uniqueMember')) echo ' selected'; ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'memberUid')) echo ' selected'; ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] == 'member')) echo ' selected'; ?>>member (AD)</option></select></p>
|
||||
<p><label for="ldap_tls"><?php echo $l->t('Use TLS');?></label><input type="checkbox" id="ldap_tls" name="ldap_tls" value="1"<?php if ($_['ldap_tls']) echo ' checked'; ?> title="<?php echo $l->t('Do not use it for SSL connections, it will fail.');?>" /></p>
|
||||
<p><label for="ldap_nocase"><?php echo $l->t('Case insensitve LDAP server (Windows)');?></label> <input type="checkbox" id="ldap_nocase" name="ldap_nocase" value="1"<?php if (isset($_['ldap_nocase']) && ($_['ldap_nocase'])) echo ' checked'; ?>></p>
|
||||
|
|
Loading…
Reference in New Issue