summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <machniak@kolabsys.com>2015-02-27 11:45:39 (GMT)
committerAleksander Machniak <machniak@kolabsys.com>2015-02-27 11:45:39 (GMT)
commit625dcd7ad38beb518017c146693acb84d8404667 (patch)
treeb87881c3ca9d03df27ba5188dcd367449b660fb5
parentf5fa3ff96b48ce14bda695b05e8ce4761b3a3c0e (diff)
downloadNet_LDAP3-625dcd7ad38beb518017c146693acb84d8404667.tar.gz
Implement domain_root_dn (#4354)
Also provide caching handler and cleanup some debug messages. Most of the code moved from Kolab WebAdmin.
-rw-r--r--lib/Net/LDAP3.php264
1 files changed, 252 insertions, 12 deletions
diff --git a/lib/Net/LDAP3.php b/lib/Net/LDAP3.php
index 37a0ea3..8ce2b56 100644
--- a/lib/Net/LDAP3.php
+++ b/lib/Net/LDAP3.php
@@ -1,4 +1,5 @@
<?php
+
/*
+-----------------------------------------------------------------------+
| Net/LDAP3.php |
@@ -88,6 +89,7 @@ class Net_LDAP3
protected $debug_level = false;
protected $list_page = 1;
protected $page_size = 10;
+ protected $icache = array();
protected $cache;
// Use public method config_set('log_hook', $callback) to have $callback be
@@ -564,7 +566,7 @@ class Net_LDAP3
else if (method_exists($this, "config_set_{$key}")) {
call_user_func_array(array($this, "config_set_$key"), array($value));
}
- else if (isset($this->$key)) {
+ else if (property_exists($this, $key)) {
$this->$key = $value;
}
else {
@@ -872,7 +874,7 @@ class Net_LDAP3
*/
public function entry_dn($subject, $attributes = array(), $base_dn = null)
{
- $this->_debug("entry_dn on subject $subject");
+ $this->_debug("Net_LDAP3::entry_dn($subject)");
$is_dn = ldap_explode_dn($subject, 1);
if (is_array($is_dn) && array_key_exists("count", $is_dn) && $is_dn["count"] > 0) {
@@ -1079,7 +1081,7 @@ class Net_LDAP3
public function login($username, $password, $domain = null, &$attributes = null)
{
- $this->_debug("Net_LDAP3::login(\$username = '" . $username . "', \$password = '****', \$domain = '" . $domain . "')");
+ $this->_debug("Net_LDAP3::login($username,***,$domain)");
$_bind_dn = $this->config_get('service_bind_dn');
$_bind_pw = $this->config_get('service_bind_pw');
@@ -1200,7 +1202,7 @@ class Net_LDAP3
public function list_group_members($dn, $entry = null, $recurse = true)
{
- $this->_debug("Called list_group_members(" . $dn . ")");
+ $this->_debug("Net_LDAP3::list_group_members($dn)");
if (is_array($entry) && in_array('objectclass', $entry)) {
if (!in_array(array('groupofnames', 'groupofuniquenames', 'groupofurls'), $entry['objectclass'])) {
@@ -2051,7 +2053,7 @@ class Net_LDAP3
return array();
}
- if ($this->cache && ($cached_config = $this->cache->get('vlvconfig'))) {
+ if ($cached_config = $this->get_cache_data('vlvconfig')) {
$this->_vlv_indexes_and_searches = $cached_config;
return $this->_vlv_indexes_and_searches;
}
@@ -2109,9 +2111,7 @@ class Net_LDAP3
}
// cache this
- if ($this->cache) {
- $this->cache->set('vlvconfig', $this->_vlv_indexes_and_searches);
- }
+ $this->set_cache_data('vlvconfig', $this->_vlv_indexes_and_searches);
return $this->_vlv_indexes_and_searches;
}
@@ -2175,7 +2175,7 @@ class Net_LDAP3
private function list_group_member($dn, $members, $recurse = true)
{
- $this->_debug("Called list_group_member(" . $dn . ")");
+ $this->_debug("Net_LDAP3::list_group_member($dn)");
$members = (array) $members;
$group_members = array();
@@ -2208,7 +2208,7 @@ class Net_LDAP3
private function list_group_uniquemember($dn, $uniquemembers, $recurse = true)
{
- $this->_debug("Called list_group_uniquemember(" . $dn . ")", $entry);
+ $this->_debug("Net_LDAP3::list_group_uniquemember($dn)", $entry);
$uniquemembers = (array)($uniquemembers);
$group_members = array();
@@ -2239,7 +2239,7 @@ class Net_LDAP3
private function list_group_memberurl($dn, $memberurls, $recurse = true)
{
- $this->_debug("Called list_group_memberurl(" . $dn . ")");
+ $this->_debug("Net_LDAP3::list_group_memberurl($dn)");
$group_members = array();
$memberurls = (array) $memberurls;
@@ -2381,7 +2381,8 @@ class Net_LDAP3
return true;
}
- private function parse_aclrights(&$attributes, $attribute_value) {
+ private function parse_aclrights(&$attributes, $attribute_value)
+ {
$components = explode(':', $attribute_value);
$_acl_target = array_shift($components);
$_acl_value = trim(implode(':', $components));
@@ -2872,4 +2873,243 @@ class Net_LDAP3
return true;
}
+ /**
+ * Get global handle for cache access
+ *
+ * @return object Cache object
+ */
+ public function get_cache()
+ {
+ if ($this->cache === true) {
+ // no memcache support in PHP
+ if (!class_exists('Memcache')) {
+ $this->cache = false;
+ return false;
+ }
+
+ // add all configured hosts to pool
+ $pconnect = $this->config_get('memcache_pconnect');
+ $hosts = $this->config_get('memcache_hosts');
+
+ if ($hosts) {
+ $this->cache = new Memcache;
+ $this->mc_available = 0;
+
+ $hosts = explode(',', $hosts);
+ foreach ($hosts as $host) {
+ $host = trim($host);
+ if (substr($host, 0, 7) != 'unix://') {
+ list($host, $port) = explode(':', $host);
+ if (!$port) $port = 11211;
+ }
+ else {
+ $port = 0;
+ }
+
+ $this->mc_available += intval($this->cache->addServer(
+ $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
+ }
+
+ // test connection and failover (will result in $this->mc_available == 0 on complete failure)
+ $this->cache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
+ }
+
+ if (!$this->mc_available) {
+ $this->cache = false;
+ }
+ }
+
+ return $this->cache;
+ }
+
+ /**
+ * Callback for memcache failure
+ */
+ public function memcache_failure($host, $port)
+ {
+ static $seen = array();
+
+ // only report once
+ if (!$seen["$host:$port"]++) {
+ $this->mc_available--;
+ $this->_error("Memcache failure on host $host:$port");
+ }
+ }
+
+ /**
+ * Get cached data
+ *
+ * @param string $key Cache key
+ *
+ * @return mixed Cached value
+ */
+ public function get_cache_data($key)
+ {
+ if ($cache = $this->get_cache()) {
+ return $cache->get($key);
+ }
+ }
+
+ /**
+ * Store cached data
+ *
+ * @param string $key Cache key
+ * @param mixed $data Data
+ * @param int $ttl Cache TTL in seconds
+ *
+ * @return bool False on failure or when cache is disabled, True if data was saved succesfully
+ */
+ public function set_cache_data($key, $data, $ttl = 3600)
+ {
+ if ($cache = $this->get_cache()) {
+ if (!method_exists($cache, 'replace') || !$cache->replace($key, $data, MEMCACHE_COMPRESSED, $ttl)) {
+ return $cache->set($key, $data, MEMCACHE_COMPRESSED, $ttl);
+ }
+ else {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Translate a domain name into it's corresponding root dn.
+ *
+ * @param string $domain Domain name
+ *
+ * @return string|bool Domain root DN or False on error
+ */
+ public function domain_root_dn($domain)
+ {
+ if (empty($domain)) {
+ return false;
+ }
+
+ $ckey = 'domain.root::' . $domain;
+ if ($result = $this->icache[$ckey]) {
+ return $result;
+ }
+
+ $this->_debug("Net_LDAP3::domain_root_dn($domain)");
+
+ if ($entry_attrs = $this->find_domain($domain)) {
+ $name_attribute = $this->config_get('domain_name_attribute');
+
+ if (empty($name_attribute)) {
+ $name_attribute = 'associateddomain';
+ }
+
+ if (is_array($entry_attrs)) {
+ if (!empty($entry_attrs['inetdomainbasedn'])) {
+ $domain_root_dn = $entry_attrs['inetdomainbasedn'];
+ }
+ else {
+ if (is_array($entry_attrs[$name_attribute])) {
+ $domain_root_dn = $this->_standard_root_dn($entry_attrs[$name_attribute][0]);
+ }
+ else {
+ $domain_root_dn = $this->_standard_root_dn($entry_attrs[$name_attribute]);
+ }
+ }
+ }
+ }
+
+ if (empty($domain_root_dn)) {
+ $domain_root_dn = $this->_standard_root_dn($domain);
+ }
+
+ $this->_debug("Net_LDAP3::domain_root_dn() result: $domain_root_dn");
+
+ return $this->icache[$ckey] = $domain_root_dn;
+ }
+
+ /**
+ * Find domain by name
+ *
+ * @param string $domain Domain name
+ * @param array $attributes Result attributes
+ *
+ * @return array|bool Domain attributes or False if not found
+ */
+ public function find_domain($domain, $attributes = array('*'))
+ {
+ if (empty($domain)) {
+ return false;
+ }
+
+ $ckey = 'domain::' . $domain;
+ $ickey = $ckey . '::' . md5(implode(',', $attributes));
+
+ if (isset($this->icache[$ickey])) {
+ return $this->icache[$ickey];
+ }
+
+ $this->_debug("Net_LDAP3::find_domain($domain)");
+
+ // use cache
+ $domain_dn = $this->get_cache_data($ckey);
+
+ if ($domain_dn) {
+ $result = $this->get_entry_attributes($domain_dn, $attributes);
+ if (empty($result)) {
+ $result = false;
+ }
+ }
+ else {
+ $domain_base_dn = $this->config_get('domain_base_dn');
+ $domain_filter = $this->config_get('domain_filter');
+ $name_attribute = $this->config_get('domain_name_attribute');
+
+ if (empty($name_attribute)) {
+ $name_attribute = 'associateddomain';
+ }
+
+ $domain_filter = "(&" . $domain_filter . "(" . $name_attribute . "=" . self::quote_string($domain) . "))";
+
+ if ($result = $this->search($domain_base_dn, $domain_filter, 'sub', $attributes)) {
+ $result = $result->entries(true);
+ $domain_dn = key($result);
+ $result = $result[$domain_dn];
+
+ // cache domain DN
+ $this->set_cache_data($ckey, $domain_dn);
+ }
+ }
+
+ $this->_debug("Net_LDAP3::find_domain() result: " . var_export($result, true));
+
+ return $this->icache[$ickey] = $result;
+ }
+
+ /**
+ * From a domain name, such as 'kanarip.com', create a standard root
+ * dn, such as 'dc=kanarip,dc=com'.
+ *
+ * As the parameter $associatedDomains, either pass it an array (such
+ * as may have been returned by ldap_get_entries() or perhaps
+ * ldap_list()), where the function will assume the first value
+ * ($array[0]) to be the uber-level domain name, or pass it a string
+ * such as 'kanarip.nl'.
+ *
+ * @return string
+ */
+ protected function _standard_root_dn($associatedDomains)
+ {
+ if (is_array($associatedDomains)) {
+ // Usually, the associatedDomain in position 0 is the naming attribute associatedDomain
+ if ($associatedDomains['count'] > 1) {
+ // Issue a debug message here
+ $relevant_associatedDomain = $associatedDomains[0];
+ }
+ else {
+ $relevant_associatedDomain = $associatedDomains[0];
+ }
+ }
+ else {
+ $relevant_associatedDomain = $associatedDomains;
+ }
+
+ return 'dc=' . implode(',dc=', explode('.', $relevant_associatedDomain));
+ }
}