From d34fbf3a8647806779d908b599068b9000d0c4e4 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 12 Jun 2013 02:08:02 +0200 Subject: [PATCH] LDAP: move PHP LDAP functions calls to an LDAP Wrapper for better isolation and mock testing --- apps/user_ldap/lib/access.php | 22 ++++---- apps/user_ldap/lib/backendbase.php | 50 +++++++++++++++++ apps/user_ldap/lib/ldap.php | 86 ++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 apps/user_ldap/lib/backendbase.php create mode 100644 apps/user_ldap/lib/ldap.php diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 52aa39012f..d09571a8a6 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -23,7 +23,7 @@ namespace OCA\user_ldap\lib; -abstract class Access { +abstract class Access extends BackendBase { protected $connection; //never ever check this var directly, always use getPagedSearchResultState protected $pagedSearchedSuccessful; @@ -60,7 +60,7 @@ abstract class Access { return false; } $dn = $this->DNasBaseParameter($dn); - $rr = @ldap_read($cr, $dn, $filter, array($attr)); + $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); if(!is_resource($rr)) { if(!empty($attr)) { //do not throw this message on userExists check, irritates @@ -73,13 +73,13 @@ abstract class Access { \OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG); return array(); } - $er = ldap_first_entry($cr, $rr); + $er = $this->ldap->first_entry($cr, $rr); if(!is_resource($er)) { //did not match the filter, return false return false; } //LDAP attributes are not case sensitive - $result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); + $result = \OCP\Util::mb_array_change_key_case($this->ldap->get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); $attr = mb_strtolower($attr, 'UTF-8'); if(isset($result[$attr]) && $result[$attr]['count'] > 0) { @@ -664,11 +664,11 @@ abstract class Access { $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, $limit, $offset); $linkResources = array_pad(array(), count($base), $link_resource); - $sr = ldap_search($linkResources, $base, $filter, $attr); - $error = ldap_errno($link_resource); + $sr = $this->ldap->search($linkResources, $base, $filter, $attr); + $error = $this->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), + 'Error when searching: '.$this->ldap->error($link_resource).' code '.$this->ldap->errno($link_resource), \OCP\Util::ERROR); \OCP\Util::writeLog('user_ldap', 'Attempt for Paging? '.print_r($pagedSearchOK, true), \OCP\Util::ERROR); return array(); @@ -677,19 +677,19 @@ abstract class Access { // Do the server-side sorting foreach(array_reverse($attr) as $sortAttr){ foreach($sr as $searchResource) { - ldap_sort($link_resource, $searchResource, $sortAttr); + $this->ldap->sort($link_resource, $searchResource, $sortAttr); } } $findings = array(); foreach($sr as $key => $res) { - $findings = array_merge($findings, ldap_get_entries($link_resource, $res )); + $findings = array_merge($findings, $this->ldap->get_entries($link_resource, $res )); } if($pagedSearchOK) { \OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO); foreach($sr as $key => $res) { $cookie = null; - if(ldap_control_paged_result_response($link_resource, $res, $cookie)) { + if($this->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); } @@ -1103,7 +1103,7 @@ abstract class Access { if($offset > 0) { \OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO); } - $pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(), + $pagedSearchOK = $this->ldap->control_paged_result($this->connection->getConnectionResource(), $limit, false, $cookie); if(!$pagedSearchOK) { return false; diff --git a/apps/user_ldap/lib/backendbase.php b/apps/user_ldap/lib/backendbase.php new file mode 100644 index 0000000000..7ed079b341 --- /dev/null +++ b/apps/user_ldap/lib/backendbase.php @@ -0,0 +1,50 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class BackendBase { + protected $ldap; + + public function __construct() { + $this->ldap = new LDAP(); + } + + /** + * @brief sets the LDAP Wrapper to be used + * + * @param $ldapWrapper an instance of the Wrapper + * @return true on success, otherwise false + * + * The LDAP Wrapper must implement the PHP LDAP functions, which are used + * in the LDAP backend + */ + public function setLDAPWrapper($ldapWrapper) { + if(is_object($ldapWrapper)) { + unset($this->ldap); + $this->ldap = $ldapWrapper; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php new file mode 100644 index 0000000000..86c57c37d8 --- /dev/null +++ b/apps/user_ldap/lib/ldap.php @@ -0,0 +1,86 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +class LDAP { + protected $curFunc = ''; + protected $curArgs = array(); + + //Simple wrapper for the ldap functions + public function __call($name, $arguments) { + $func = 'ldap_' . $name; + if(function_exists($func)) { + $this->preFunctionCall($func, $arguments); + $result = call_user_func_array($func, $arguments); + $this->postFunctionCall(); + return $result; + } + } + + public function control_paged_result_response($linkResource, $resultResource, &$cookie) { + $this->preFunctionCall('ldap_control_paged_result_response', + array($linkResource, $resultResource, $cookie)); + $result = ldap_control_paged_result_response( + $linkResource, $resultResource, $cookie); + $this->postFunctionCall(); + + return $result; + } + + public function areLDAPFunctionsAvailable() { + return function_exists('ldap_connect'); + } + + public function hasPagedResultSupport() { + $hasSupport = function_exists('ldap_control_paged_result') + && function_exists('ldap_control_paged_result_response'); + return $hasSupport; + } + + private function preFunctionCall($functionName, $args) { + $this->curFunc = $functionName; + $this->curArgs = $args; + } + + private function postFunctionCall() { + if(is_resource($this->curArgs[0])) { + $errorCode = ldap_errno($this->curArgs[0]); + $errorMsg = ldap_error($this->curArgs[0]); + if($errorCode !== 0) { + if($this->curFunc === 'ldap_sort' && $errorCode === -4) { + //You can safely ignore that decoding error. + //… says https://bugs.php.net/bug.php?id=18023 + } else if($this->curFunc === 'ldap_get_entries' && $errorCode === -4) { + } else if ($errorCode === 32) { + //for now + } else { + throw new \Exception('LDAP error '.$errorMsg.' (' .$errorCode.') after calling '.$this->curFunc.' with arguments '.print_r($this->curArgs, true)); + } + } + } + + $this->curFunc = ''; + $this->curArgs = array(); + } +} \ No newline at end of file