From 6c92a85d49822dba180fe949d211db60c5b40d51 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 26 Jul 2012 16:11:23 +0200 Subject: [PATCH] LDAP: use OC_Cache to cache results from LDAP. Default is set to 10 min. Should improve performance especially when LDAP users use the sync client, because userExists checks with the LDAP server are reduced. --- apps/user_ldap/group_ldap.php | 53 +++++++++++++----------- apps/user_ldap/lib/connection.php | 59 ++++++++++++++++++++++++++- apps/user_ldap/settings.php | 12 ++++-- apps/user_ldap/templates/settings.php | 1 + apps/user_ldap/user_ldap.php | 18 +++++--- 5 files changed, 109 insertions(+), 34 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index b9f4bdf199..aff25593ef 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -26,11 +26,6 @@ namespace OCA\user_ldap; class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { protected $enabled = false; - protected $_group_user = array(); - protected $_user_groups = array(); - protected $_group_users = array(); - protected $_groups = array(); - public function setConnector(lib\Connection &$connection) { parent::setConnector($connection); if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) { @@ -51,18 +46,20 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if(!$this->enabled) { return false; } - if(isset($this->_group_user[$gid][$uid])) { - return $this->_group_user[$gid][$uid]; + if($this->connection->isCached('inGroup'.$uid.':'.$gid)) { + return $this->connection->getFromCache('inGroup'.$uid.':'.$gid); } $dn_user = $this->username2dn($uid); $dn_group = $this->groupname2dn($gid); // just in case if(!$dn_group || !$dn_user) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. $members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } @@ -81,8 +78,10 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { $members = $dns; } - $this->_group_user[$gid][$uid] = in_array($dn_user, $members); - return $this->_group_user[$gid][$uid]; + $isInGroup = in_array($dn_user, $members); + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup); + + return $isInGroup; } /** @@ -97,12 +96,12 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if(!$this->enabled) { return array(); } - if(isset($this->_user_groups[$uid])) { - return $this->_user_groups[$uid]; + if($this->connection->isCached('getUserGroups'.$uid)) { + return $this->connection->getFromCache('getUserGroups'.$uid); } $userDN = $this->username2dn($uid); if(!$userDN) { - $this->_user_groups[$uid] = array(); + $this->connection->writeToCache('getUserGroups'.$uid, array()); return array(); } @@ -124,9 +123,10 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { $this->connection->ldapGroupMemberAssocAttr.'='.$uid )); $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn')); - $this->_user_groups[$uid] = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $this->connection->writeToCache('getUserGroups'.$uid, $groups); - return $this->_user_groups[$uid]; + return $groups; } /** @@ -137,19 +137,19 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if(!$this->enabled) { return array(); } - if(isset($this->_group_users[$gid])) { - return $this->_group_users[$gid]; + if($this->connection->isCached('usersInGroup'.$gid)) { + return $this->connection->getFromCache('usersInGroup'.$gid); } $groupDN = $this->groupname2dn($gid); if(!$groupDN) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } $members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } @@ -173,8 +173,10 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if(!$isMemberUid) { $result = array_intersect($result, \OCP\User::getUsers()); } - $this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING); - return $this->_group_users[$gid]; + $groupUsers = array_unique($result, SORT_LOCALE_STRING); + $this->connection->writeToCache('usersInGroup'.$gid, $groupUsers); + + return $groupUsers; } /** @@ -187,11 +189,14 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { if(!$this->enabled) { return array(); } - if(empty($this->_groups)) { - $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); - $this->_groups = $this->ownCloudGroupNames($ldap_groups); + if($this->connection->isCached('getGroups')) { + return $this->connection->getFromCache('getGroups'); } - return $this->_groups; + $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); + $ldap_groups = $this->ownCloudGroupNames($ldap_groups); + $this->connection->writeToCache('getGroups', $ldap_groups); + + return $ldap_groups; } /** diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php index 96ffea4b64..cd9c83ff33 100644 --- a/apps/user_ldap/lib/connection.php +++ b/apps/user_ldap/lib/connection.php @@ -28,7 +28,10 @@ class Connection { private $configID; private $configured = false; - //cached settings + //cache handler + protected $cache; + + //settings protected $config = array( 'ldapHost' => null, 'ldapPort' => null, @@ -48,10 +51,12 @@ class Connection { 'ldapQuotaAttribute' => null, 'ldapQuotaDefault' => null, 'ldapEmailAttribute' => null, + 'ldapCacheTTL' => null, ); public function __construct($configID = 'user_ldap') { $this->configID = $configID; + $this->cache = \OC_Cache::getGlobalCache(); } public function __destruct() { @@ -92,6 +97,57 @@ class Connection { return $this->ldapConnectionRes; } + private function getCacheKey($key) { + $prefix = 'LDAP-'.$this->configID.'-'; + if(is_null($key)) { + return $prefix; + } + return $prefix.md5($key); + } + + public function getFromCache($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + if(!$this->isCached($key)) { + return null; + + } + $key = $this->getCacheKey($key); + + return unserialize(base64_decode($this->cache->get($key))); + } + + public function isCached($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return false; + } + $key = $this->getCacheKey($key); + return $this->cache->hasKey($key); + } + + public function writeToCache($key, $value) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + $key = $this->getCacheKey($key); + $value = base64_encode(serialize($value)); + $this->cache->set($key, $value, $this->config['ldapCacheTTL']); + } + + public function clearCache() { + $this->cache->clear($this->getCacheKey(null)); + } + /** * Caches the general LDAP configuration. */ @@ -118,6 +174,7 @@ class Connection { $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); $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->configured = $this->validateConfiguration(); } diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 42084855e8..135c735e70 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -20,7 +20,7 @@ * License along with this library. If not, see . * */ -$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute'); +$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl'); OCP\Util::addscript('user_ldap', 'settings'); @@ -29,18 +29,23 @@ if ($_POST) { if(isset($_POST[$param])){ if('ldap_agent_password' == $param) { OCP\Config::setAppValue('user_ldap', $param, base64_encode($_POST[$param])); + } elseif('ldap_cache_ttl' == $param) { + if(OCP\Config::getAppValue('user_ldap', $param,'') != $_POST[$param]) { + $ldap = new \OCA\user_ldap\lib\Connection('user_ldap'); + $ldap->clearCache(); + OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); + } } else { OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); } } elseif('ldap_tls' == $param) { // unchecked checkboxes are not included in the post paramters - OCP\Config::setAppValue('user_ldap', $param, 0); + OCP\Config::setAppValue('user_ldap', $param, 0); } elseif('ldap_nocase' == $param) { OCP\Config::setAppValue('user_ldap', $param, 0); } - } } @@ -57,5 +62,6 @@ $tmpl->assign( 'ldap_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_d $tmpl->assign( 'ldap_group_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', 'cn')); $tmpl->assign( 'ldap_group_member_assoc_attribute', OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember')); $tmpl->assign( 'ldap_agent_password', base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password'))); +$tmpl->assign( 'ldap_cache_ttl', OCP\Config::getAppValue('user_ldap', 'ldap_cache_ttl', '600')); return $tmpl->fetchPage(); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 31f453b5a5..cc61598a26 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -26,6 +26,7 @@

bytes

+

t('in seconds');?>

t('Help');?> diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 61f84047f1..57b2ef489b 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -27,9 +27,6 @@ namespace OCA\user_ldap; class USER_LDAP extends lib\Access implements \OCP\UserInterface { - // cache getUsers() - protected $_users = null; - private function updateQuota($dn) { $quota = null; if(!empty($this->connection->ldapQuotaDefault)) { @@ -97,11 +94,13 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { * Get a list of all users. */ public function getUsers(){ - if(is_null($this->_users)) { + $ldap_users = $this->connection->getFromCache('getUsers'); + if(is_null($ldap_users)) { $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn')); - $this->_users = $this->ownCloudUserNames($ldap_users); + $ldap_users = $this->ownCloudUserNames($ldap_users); + $this->connection->writeToCache('getUsers', $ldap_users); } - return $this->_users; + return $ldap_users; } /** @@ -110,18 +109,25 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface { * @return boolean */ public function userExists($uid){ + if($this->connection->isCached('userExists'.$uid)) { + return $this->connection->getFromCache('userExists'.$uid); + } + //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. $dn = $this->username2dn($uid); if(!$dn) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } //if user really still exists, we will be able to read his cn $cn = $this->readAttribute($dn, 'cn'); if(!$cn || empty($cn)) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } + $this->connection->writeToCache('userExists'.$uid, true); return true; }