From: Craig Andrews Date: Mon, 22 Mar 2010 03:05:23 +0000 (-0400) Subject: Refactor common parts of LdapAuthorization and LdapAuthentication plugins into a... X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=295e05ea39982ff2b41cec21d5622375005682c2;p=quix0rs-gnu-social.git Refactor common parts of LdapAuthorization and LdapAuthentication plugins into a separate class LdapAuthorization should get a performance improvement as LDAP schema caching is now used --- diff --git a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php index 4832096765..6296bccfad 100644 --- a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php +++ b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php @@ -31,48 +31,25 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once 'Net/LDAP2.php'; - class LdapAuthenticationPlugin extends AuthenticationPlugin { - public $host=null; - public $port=null; - public $version=null; - public $starttls=null; - public $binddn=null; - public $bindpw=null; - public $basedn=null; - public $options=null; - public $filter=null; - public $scope=null; - public $password_encoding=null; - public $attributes=array(); - function onInitializePlugin(){ parent::onInitializePlugin(); - if(!isset($this->host)){ - throw new Exception("must specify a host"); - } - if(!isset($this->basedn)){ - throw new Exception("must specify a basedn"); - } if(!isset($this->attributes['nickname'])){ throw new Exception("must specify a nickname attribute"); } - if(!isset($this->attributes['username'])){ - throw new Exception("must specify a username attribute"); - } if($this->password_changeable && (! isset($this->attributes['password']) || !isset($this->password_encoding))){ throw new Exception("if password_changeable is set, the password attribute and password_encoding must also be specified"); } + $this->ldapCommon = new LdapCommon(get_object_vars($this)); } function onAutoload($cls) { switch ($cls) { - case 'MemcacheSchemaCache': - require_once(INSTALLDIR.'/plugins/LdapAuthentication/MemcacheSchemaCache.php'); + case 'LdapCommon': + require_once(INSTALLDIR.'/plugins/LdapCommon/LdapCommon.php'); return false; } } @@ -107,19 +84,7 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin function checkPassword($username, $password) { - $entry = $this->ldap_get_user($username); - if(!$entry){ - return false; - }else{ - $config = $this->ldap_get_config(); - $config['binddn']=$entry->dn(); - $config['bindpw']=$password; - if($this->ldap_get_connection($config)){ - return true; - }else{ - return false; - } - } + return $this->ldapCommon->checkPassword($username); } function autoRegister($username, $nickname) @@ -127,7 +92,7 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin if(is_null($nickname)){ $nickname = $username; } - $entry = $this->ldap_get_user($username,$this->attributes); + $entry = $this->ldapCommon->get_user($username,$this->attributes); if($entry){ $registration_data = array(); foreach($this->attributes as $sn_attribute=>$ldap_attribute){ @@ -148,40 +113,7 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin function changePassword($username,$oldpassword,$newpassword) { - if(! isset($this->attributes['password']) || !isset($this->password_encoding)){ - //throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time')); - return false; - } - $entry = $this->ldap_get_user($username); - if(!$entry){ - return false; - }else{ - $config = $this->ldap_get_config(); - $config['binddn']=$entry->dn(); - $config['bindpw']=$oldpassword; - if($ldap = $this->ldap_get_connection($config)){ - $entry = $this->ldap_get_user($username,array(),$ldap); - - $newCryptedPassword = $this->hashPassword($newpassword, $this->password_encoding); - if ($newCryptedPassword===false) { - return false; - } - if($this->password_encoding=='ad') { - //TODO I believe this code will work once this bug is fixed: http://pear.php.net/bugs/bug.php?id=16796 - $oldCryptedPassword = $this->hashPassword($oldpassword, $this->password_encoding); - $entry->delete( array($this->attributes['password'] => $oldCryptedPassword )); - } - $entry->replace( array($this->attributes['password'] => $newCryptedPassword ), true); - if( Net_LDAP2::isError($entry->upate()) ) { - return false; - } - return true; - }else{ - return false; - } - } - - return false; + return $this->ldapCommon->changePassword($username,$oldpassword,$newpassword); } function suggestNicknameForUsername($username) @@ -198,203 +130,6 @@ class LdapAuthenticationPlugin extends AuthenticationPlugin } return common_nicknamize($nickname); } - - //---utility functions---// - function ldap_get_config(){ - $config = array(); - $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); - foreach($keys as $key){ - $value = $this->$key; - if($value!==null){ - $config[$key]=$value; - } - } - return $config; - } - - function ldap_get_connection($config = null){ - if($config == null && isset($this->default_ldap)){ - return $this->default_ldap; - } - - //cannot use Net_LDAP2::connect() as StatusNet uses - //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); - //PEAR handling can be overridden on instance objects, so we do that. - $ldap = new Net_LDAP2(isset($config)?$config:$this->ldap_get_config()); - $ldap->setErrorHandling(PEAR_ERROR_RETURN); - $err=$ldap->bind(); - if (Net_LDAP2::isError($err)) { - // if we were called with a config, assume caller will handle - // incorrect username/password (LDAP_INVALID_CREDENTIALS) - if (isset($config) && $err->getCode() == 0x31) { - return null; - } - throw new Exception('Could not connect to LDAP server: '.$err->getMessage()); - } - if($config == null) $this->default_ldap=$ldap; - - $c = common_memcache(); - if (!empty($c)) { - $cacheObj = new MemcacheSchemaCache( - array('c'=>$c, - 'cacheKey' => common_cache_key('ldap_schema:' . crc32(serialize($config))))); - $ldap->registerSchemaCache($cacheObj); - } - return $ldap; - } - - /** - * get an LDAP entry for a user with a given username - * - * @param string $username - * $param array $attributes LDAP attributes to retrieve - * @return string DN - */ - function ldap_get_user($username,$attributes=array(),$ldap=null){ - if($ldap==null) { - $ldap = $this->ldap_get_connection(); - } - $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); - $options = array( - 'attributes' => $attributes - ); - $search = $ldap->search($this->basedn, $filter, $options); - - if (PEAR::isError($search)) { - common_log(LOG_WARNING, 'Error while getting DN for user: '.$search->getMessage()); - return false; - } - - $searchcount = $search->count(); - if($searchcount == 0) { - return false; - }else if($searchcount == 1) { - $entry = $search->shiftEntry(); - return $entry; - }else{ - common_log(LOG_WARNING, 'Found ' . $searchcount . ' ldap user with the username: ' . $username); - return false; - } - } - - /** - * Code originaly from the phpLDAPadmin development team - * http://phpldapadmin.sourceforge.net/ - * - * Hashes a password and returns the hash based on the specified enc_type. - * - * @param string $passwordClear The password to hash in clear text. - * @param string $encodageType Standard LDAP encryption type which must be one of - * crypt, ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear. - * @return string The hashed password. - * - */ - - function hashPassword( $passwordClear, $encodageType ) - { - $encodageType = strtolower( $encodageType ); - switch( $encodageType ) { - case 'crypt': - $cryptedPassword = '{CRYPT}' . crypt($passwordClear,$this->randomSalt(2)); - break; - - case 'ext_des': - // extended des crypt. see OpenBSD crypt man page. - if ( ! defined( 'CRYPT_EXT_DES' ) || CRYPT_EXT_DES == 0 ) {return FALSE;} //Your system crypt library does not support extended DES encryption. - $cryptedPassword = '{CRYPT}' . crypt( $passwordClear, '_' . $this->randomSalt(8) ); - break; - - case 'md5crypt': - if( ! defined( 'CRYPT_MD5' ) || CRYPT_MD5 == 0 ) {return FALSE;} //Your system crypt library does not support md5crypt encryption. - $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$1$' . $this->randomSalt(9) ); - break; - - case 'blowfish': - if( ! defined( 'CRYPT_BLOWFISH' ) || CRYPT_BLOWFISH == 0 ) {return FALSE;} //Your system crypt library does not support blowfish encryption. - $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$2a$12$' . $this->randomSalt(13) ); // hardcoded to second blowfish version and set number of rounds - break; - - case 'md5': - $cryptedPassword = '{MD5}' . base64_encode( pack( 'H*' , md5( $passwordClear) ) ); - break; - - case 'sha': - if( function_exists('sha1') ) { - // use php 4.3.0+ sha1 function, if it is available. - $cryptedPassword = '{SHA}' . base64_encode( pack( 'H*' , sha1( $passwordClear) ) ); - } elseif( function_exists( 'mhash' ) ) { - $cryptedPassword = '{SHA}' . base64_encode( mhash( MHASH_SHA1, $passwordClear) ); - } else { - return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. - } - break; - - case 'ssha': - if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { - mt_srand( (double) microtime() * 1000000 ); - $salt = mhash_keygen_s2k( MHASH_SHA1, $passwordClear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); - $cryptedPassword = "{SSHA}".base64_encode( mhash( MHASH_SHA1, $passwordClear.$salt ).$salt ); - } else { - return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. - } - break; - - case 'smd5': - if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { - mt_srand( (double) microtime() * 1000000 ); - $salt = mhash_keygen_s2k( MHASH_MD5, $passwordClear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); - $cryptedPassword = "{SMD5}".base64_encode( mhash( MHASH_MD5, $passwordClear.$salt ).$salt ); - } else { - return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. - } - break; - - case 'ad': - $cryptedPassword = ''; - $passwordClear = "\"" . $passwordClear . "\""; - $len = strlen($passwordClear); - for ($i = 0; $i < $len; $i++) { - $cryptedPassword .= "{$passwordClear{$i}}\000"; - } - - case 'clear': - default: - $cryptedPassword = $passwordClear; - } - - return $cryptedPassword; - } - - /** - * Code originaly from the phpLDAPadmin development team - * http://phpldapadmin.sourceforge.net/ - * - * Used to generate a random salt for crypt-style passwords. Salt strings are used - * to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses - * not only the user's password but also a randomly generated string. The string is - * stored as the first N characters of the hash for reference of hashing algorithms later. - * - * --- added 20021125 by bayu irawan --- - * --- ammended 20030625 by S C Rigler --- - * - * @param int $length The length of the salt string to generate. - * @return string The generated salt string. - */ - - function randomSalt( $length ) - { - $possible = '0123456789'. - 'abcdefghijklmnopqrstuvwxyz'. - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. - './'; - $str = ""; - mt_srand((double)microtime() * 1000000); - - while( strlen( $str ) < $length ) - $str .= substr( $possible, ( rand() % strlen( $possible ) ), 1 ); - - return $str; - } function onPluginVersion(&$versions) { diff --git a/plugins/LdapAuthentication/MemcacheSchemaCache.php b/plugins/LdapAuthentication/MemcacheSchemaCache.php deleted file mode 100644 index 6b91d17d64..0000000000 --- a/plugins/LdapAuthentication/MemcacheSchemaCache.php +++ /dev/null @@ -1,75 +0,0 @@ -. - * - * @category Plugin - * @package StatusNet - * @author Craig Andrews - * @copyright 2009 Craig Andrews http://candrews.integralblue.com - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class MemcacheSchemaCache implements Net_LDAP2_SchemaCache -{ - protected $c; - protected $cacheKey; - - /** - * Initialize the simple cache - * - * Config is as following: - * memcache memcache instance - * cachekey the key in the cache to look at - * - * @param array $cfg Config array - */ - public function MemcacheSchemaCache($cfg) - { - $this->c = $cfg['c']; - $this->cacheKey = $cfg['cacheKey']; - } - - /** - * Return the schema object from the cache - * - * @return Net_LDAP2_Schema|Net_LDAP2_Error|false - */ - public function loadSchema() - { - return $this->c->get($this->cacheKey); - } - - /** - * Store a schema object in the cache - * - * This method will be called, if Net_LDAP2 has fetched a fresh - * schema object from LDAP and wants to init or refresh the cache. - * - * To invalidate the cache and cause Net_LDAP2 to refresh the cache, - * you can call this method with null or false as value. - * The next call to $ldap->schema() will then refresh the caches object. - * - * @param mixed $schema The object that should be cached - * @return true|Net_LDAP2_Error|false - */ - public function storeSchema($schema) { - return $this->c->set($this->cacheKey, $schema); - } -} diff --git a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php index e6a68cbaed..97103d158e 100644 --- a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php +++ b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php @@ -31,41 +31,28 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once 'Net/LDAP2.php'; - class LdapAuthorizationPlugin extends AuthorizationPlugin { - public $host=null; - public $port=null; - public $version=null; - public $starttls=null; - public $binddn=null; - public $bindpw=null; - public $basedn=null; - public $options=null; - public $filter=null; - public $scope=null; - public $provider_name = null; - public $uniqueMember_attribute = null; public $roles_to_groups = array(); public $login_group = null; - public $attributes = array(); function onInitializePlugin(){ - if(!isset($this->host)){ - throw new Exception("must specify a host"); - } - if(!isset($this->basedn)){ - throw new Exception("must specify a basedn"); - } if(!isset($this->provider_name)){ throw new Exception("provider_name must be set. Use the provider_name from the LDAP Authentication plugin."); } if(!isset($this->uniqueMember_attribute)){ throw new Exception("uniqueMember_attribute must be set."); } - if(!isset($this->attributes['username'])){ - throw new Exception("username attribute must be set."); + $this->ldapCommon = new LdapCommon(get_object_vars($this)); + } + + function onAutoload($cls) + { + switch ($cls) + { + case 'LdapCommon': + require_once(INSTALLDIR.'/plugins/LdapCommon/LdapCommon.php'); + return false; } } @@ -75,17 +62,17 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin $user_username->user_id=$user->id; $user_username->provider_name=$this->provider_name; if($user_username->find() && $user_username->fetch()){ - $entry = $this->ldap_get_user($user_username->username); + $entry = $this->ldapCommon->get_user($user_username->username); if($entry){ if(isset($this->login_group)){ if(is_array($this->login_group)){ foreach($this->login_group as $group){ - if($this->ldap_is_dn_member_of_group($entry->dn(),$group)){ + if($this->ldapCommon->is_dn_member_of_group($entry->dn(),$group)){ return true; } } }else{ - if($this->ldap_is_dn_member_of_group($entry->dn(),$this->login_group)){ + if($this->ldapCommon->is_dn_member_of_group($entry->dn(),$this->login_group)){ return true; } } @@ -107,17 +94,17 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin $user_username->user_id=$profile->id; $user_username->provider_name=$this->provider_name; if($user_username->find() && $user_username->fetch()){ - $entry = $this->ldap_get_user($user_username->username); + $entry = $this->ldapCommon->get_user($user_username->username); if($entry){ if(isset($this->roles_to_groups[$name])){ if(is_array($this->roles_to_groups[$name])){ foreach($this->roles_to_groups[$name] as $group){ - if($this->ldap_is_dn_member_of_group($entry->dn(),$group)){ + if($this->ldapCommon->is_dn_member_of_group($entry->dn(),$group)){ return true; } } }else{ - if($this->ldap_is_dn_member_of_group($entry->dn(),$this->roles_to_groups[$name])){ + if($this->ldapCommon->is_dn_member_of_group($entry->dn(),$this->roles_to_groups[$name])){ return true; } } @@ -127,94 +114,6 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin return false; } - function ldap_is_dn_member_of_group($userDn, $groupDn) - { - $ldap = $this->ldap_get_connection(); - $link = $ldap->getLink(); - $r = @ldap_compare($link, $groupDn, $this->uniqueMember_attribute, $userDn); - if ($r === true){ - return true; - }else if($r === false){ - return false; - }else{ - common_log(LOG_ERR, "LDAP error determining if userDn=$userDn is a member of groupDn=$groupDn using uniqueMember_attribute=$this->uniqueMember_attribute error: ".ldap_error($link)); - return false; - } - } - - function ldap_get_config(){ - $config = array(); - $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); - foreach($keys as $key){ - $value = $this->$key; - if($value!==null){ - $config[$key]=$value; - } - } - return $config; - } - - //-----the below function were copied from LDAPAuthenticationPlugin. They will be moved to a utility class soon.----\\ - function ldap_get_connection($config = null){ - if($config == null && isset($this->default_ldap)){ - return $this->default_ldap; - } - - //cannot use Net_LDAP2::connect() as StatusNet uses - //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); - //PEAR handling can be overridden on instance objects, so we do that. - $ldap = new Net_LDAP2(isset($config)?$config:$this->ldap_get_config()); - $ldap->setErrorHandling(PEAR_ERROR_RETURN); - $err=$ldap->bind(); - if (Net_LDAP2::isError($err)) { - // if we were called with a config, assume caller will handle - // incorrect username/password (LDAP_INVALID_CREDENTIALS) - if (isset($config) && $err->getCode() == 0x31) { - return null; - } - throw new Exception('Could not connect to LDAP server: '.$err->getMessage()); - return false; - } - if($config == null) $this->default_ldap=$ldap; - return $ldap; - } - - /** - * get an LDAP entry for a user with a given username - * - * @param string $username - * $param array $attributes LDAP attributes to retrieve - * @return string DN - */ - function ldap_get_user($username,$attributes=array(),$ldap=null){ - if($ldap==null) { - $ldap = $this->ldap_get_connection(); - } - if(! $ldap) { - throw new Exception("Could not connect to LDAP"); - } - $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); - $options = array( - 'attributes' => $attributes - ); - $search = $ldap->search(null,$filter,$options); - - if (PEAR::isError($search)) { - common_log(LOG_WARNING, 'Error while getting DN for user: '.$search->getMessage()); - return false; - } - - if($search->count()==0){ - return false; - }else if($search->count()==1){ - $entry = $search->shiftEntry(); - return $entry; - }else{ - common_log(LOG_WARNING, 'Found ' . $search->count() . ' ldap user with the username: ' . $username); - return false; - } - } - function onPluginVersion(&$versions) { $versions[] = array('name' => 'LDAP Authorization', diff --git a/plugins/LdapCommon/LdapCommon.php b/plugins/LdapCommon/LdapCommon.php new file mode 100644 index 0000000000..39d872df53 --- /dev/null +++ b/plugins/LdapCommon/LdapCommon.php @@ -0,0 +1,356 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +class LdapCommon +{ + protected static $ldap_connections = array(); + public $host=null; + public $port=null; + public $version=null; + public $starttls=null; + public $binddn=null; + public $bindpw=null; + public $basedn=null; + public $options=null; + public $filter=null; + public $scope=null; + public $uniqueMember_attribute = null; + public $attributes=array(); + public $password_encoding=null; + + public function __construct($config) + { + Event::addHandler('Autoload',array($this,'onAutoload')); + foreach($config as $key=>$value) { + $this->$key = $value; + } + $this->ldap_config = $this->get_ldap_config(); + + if(!isset($this->host)){ + throw new Exception("must specify a host"); + } + if(!isset($this->basedn)){ + throw new Exception("must specify a basedn"); + } + if(!isset($this->attributes['username'])){ + throw new Exception("username attribute must be set."); + } + } + + function onAutoload($cls) + { + switch ($cls) + { + case 'MemcacheSchemaCache': + require_once(INSTALLDIR.'/plugins/LdapCommon/MemcacheSchemaCache.php'); + return false; + case 'Net_LDAP2': + require_once 'Net/LDAP2.php'; + return false; + } + } + + function get_ldap_config(){ + $config = array(); + $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); + foreach($keys as $key){ + $value = $this->$key; + if($value!==null){ + $config[$key]=$value; + } + } + return $config; + } + + function get_ldap_connection($config = null){ + if($config == null) { + $config = $this->ldap_config; + } + $config_id = crc32(serialize($config)); + $ldap = self::$ldap_connections[$config_id]; + if(! isset($ldap)) { + //cannot use Net_LDAP2::connect() as StatusNet uses + //PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); + //PEAR handling can be overridden on instance objects, so we do that. + $ldap = new Net_LDAP2($config); + $ldap->setErrorHandling(PEAR_ERROR_RETURN); + $err=$ldap->bind(); + if (Net_LDAP2::isError($err)) { + // if we were called with a config, assume caller will handle + // incorrect username/password (LDAP_INVALID_CREDENTIALS) + if (isset($config) && $err->getCode() == 0x31) { + throw new LdapInvalidCredentialsException('Could not connect to LDAP server: '.$err->getMessage()); + } + throw new Exception('Could not connect to LDAP server: '.$err->getMessage()); + } + $c = common_memcache(); + if (!empty($c)) { + $cacheObj = new MemcacheSchemaCache( + array('c'=>$c, + 'cacheKey' => common_cache_key('ldap_schema:' . $config_id))); + $ldap->registerSchemaCache($cacheObj); + } + self::$ldap_connections[$config_id] = $ldap; + } + return $ldap; + } + + function checkPassword($username, $password) + { + $entry = $this->get_user($username); + if(!$entry){ + return false; + }else{ + $config = $this->get_ldap_config(); + $config['binddn']=$entry->dn(); + $config['bindpw']=$password; + try { + $this->get_ldap_connection($config); + } catch (LdapInvalidCredentialsException $e) { + return false; + } + return true; + } + } + + function changePassword($username,$oldpassword,$newpassword) + { + if(! isset($this->attributes['password']) || !isset($this->password_encoding)){ + //throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time')); + return false; + } + $entry = $this->get_user($username); + if(!$entry){ + return false; + }else{ + $config = $this->get_ldap_config(); + $config['binddn']=$entry->dn(); + $config['bindpw']=$oldpassword; + try { + $ldap = $this->get_ldap_connection($config); + + $entry = $this->get_user($username,array(),$ldap); + + $newCryptedPassword = $this->hashPassword($newpassword, $this->password_encoding); + if ($newCryptedPassword===false) { + return false; + } + if($this->password_encoding=='ad') { + //TODO I believe this code will work once this bug is fixed: http://pear.php.net/bugs/bug.php?id=16796 + $oldCryptedPassword = $this->hashPassword($oldpassword, $this->password_encoding); + $entry->delete( array($this->attributes['password'] => $oldCryptedPassword )); + } + $entry->replace( array($this->attributes['password'] => $newCryptedPassword ), true); + if( Net_LDAP2::isError($entry->upate()) ) { + return false; + } + return true; + } catch (LdapInvalidCredentialsException $e) { + return false; + } + } + + return false; + } + + function is_dn_member_of_group($userDn, $groupDn) + { + $ldap = $this->get_ldap_connection(); + $link = $ldap->getLink(); + $r = @ldap_compare($link, $groupDn, $this->uniqueMember_attribute, $userDn); + if ($r === true){ + return true; + }else if($r === false){ + return false; + }else{ + common_log(LOG_ERR, "LDAP error determining if userDn=$userDn is a member of groupDn=$groupDn using uniqueMember_attribute=$this->uniqueMember_attribute error: ".ldap_error($link)); + return false; + } + } + + /** + * get an LDAP entry for a user with a given username + * + * @param string $username + * $param array $attributes LDAP attributes to retrieve + * @return string DN + */ + function get_user($username,$attributes=array()){ + $ldap = $this->get_ldap_connection(); + $filter = Net_LDAP2_Filter::create($this->attributes['username'], 'equals', $username); + $options = array( + 'attributes' => $attributes + ); + $search = $ldap->search(null,$filter,$options); + + if (PEAR::isError($search)) { + common_log(LOG_WARNING, 'Error while getting DN for user: '.$search->getMessage()); + return false; + } + + if($search->count()==0){ + return false; + }else if($search->count()==1){ + $entry = $search->shiftEntry(); + return $entry; + }else{ + common_log(LOG_WARNING, 'Found ' . $search->count() . ' ldap user with the username: ' . $username); + return false; + } + } + + /** + * Code originaly from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + * + * Hashes a password and returns the hash based on the specified enc_type. + * + * @param string $passwordClear The password to hash in clear text. + * @param string $encodageType Standard LDAP encryption type which must be one of + * crypt, ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear. + * @return string The hashed password. + * + */ + + function hashPassword( $passwordClear, $encodageType ) + { + $encodageType = strtolower( $encodageType ); + switch( $encodageType ) { + case 'crypt': + $cryptedPassword = '{CRYPT}' . crypt($passwordClear,$this->randomSalt(2)); + break; + + case 'ext_des': + // extended des crypt. see OpenBSD crypt man page. + if ( ! defined( 'CRYPT_EXT_DES' ) || CRYPT_EXT_DES == 0 ) {return FALSE;} //Your system crypt library does not support extended DES encryption. + $cryptedPassword = '{CRYPT}' . crypt( $passwordClear, '_' . $this->randomSalt(8) ); + break; + + case 'md5crypt': + if( ! defined( 'CRYPT_MD5' ) || CRYPT_MD5 == 0 ) {return FALSE;} //Your system crypt library does not support md5crypt encryption. + $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$1$' . $this->randomSalt(9) ); + break; + + case 'blowfish': + if( ! defined( 'CRYPT_BLOWFISH' ) || CRYPT_BLOWFISH == 0 ) {return FALSE;} //Your system crypt library does not support blowfish encryption. + $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$2a$12$' . $this->randomSalt(13) ); // hardcoded to second blowfish version and set number of rounds + break; + + case 'md5': + $cryptedPassword = '{MD5}' . base64_encode( pack( 'H*' , md5( $passwordClear) ) ); + break; + + case 'sha': + if( function_exists('sha1') ) { + // use php 4.3.0+ sha1 function, if it is available. + $cryptedPassword = '{SHA}' . base64_encode( pack( 'H*' , sha1( $passwordClear) ) ); + } elseif( function_exists( 'mhash' ) ) { + $cryptedPassword = '{SHA}' . base64_encode( mhash( MHASH_SHA1, $passwordClear) ); + } else { + return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. + } + break; + + case 'ssha': + if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { + mt_srand( (double) microtime() * 1000000 ); + $salt = mhash_keygen_s2k( MHASH_SHA1, $passwordClear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); + $cryptedPassword = "{SSHA}".base64_encode( mhash( MHASH_SHA1, $passwordClear.$salt ).$salt ); + } else { + return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. + } + break; + + case 'smd5': + if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { + mt_srand( (double) microtime() * 1000000 ); + $salt = mhash_keygen_s2k( MHASH_MD5, $passwordClear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); + $cryptedPassword = "{SMD5}".base64_encode( mhash( MHASH_MD5, $passwordClear.$salt ).$salt ); + } else { + return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes. + } + break; + + case 'ad': + $cryptedPassword = ''; + $passwordClear = "\"" . $passwordClear . "\""; + $len = strlen($passwordClear); + for ($i = 0; $i < $len; $i++) { + $cryptedPassword .= "{$passwordClear{$i}}\000"; + } + + case 'clear': + default: + $cryptedPassword = $passwordClear; + } + + return $cryptedPassword; + } + + /** + * Code originaly from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + * + * Used to generate a random salt for crypt-style passwords. Salt strings are used + * to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses + * not only the user's password but also a randomly generated string. The string is + * stored as the first N characters of the hash for reference of hashing algorithms later. + * + * --- added 20021125 by bayu irawan --- + * --- ammended 20030625 by S C Rigler --- + * + * @param int $length The length of the salt string to generate. + * @return string The generated salt string. + */ + + function randomSalt( $length ) + { + $possible = '0123456789'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + './'; + $str = ""; + mt_srand((double)microtime() * 1000000); + + while( strlen( $str ) < $length ) + $str .= substr( $possible, ( rand() % strlen( $possible ) ), 1 ); + + return $str; + } + +} + +class LdapInvalidCredentialsException extends Exception +{ + +} diff --git a/plugins/LdapCommon/MemcacheSchemaCache.php b/plugins/LdapCommon/MemcacheSchemaCache.php new file mode 100644 index 0000000000..6b91d17d64 --- /dev/null +++ b/plugins/LdapCommon/MemcacheSchemaCache.php @@ -0,0 +1,75 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews + * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class MemcacheSchemaCache implements Net_LDAP2_SchemaCache +{ + protected $c; + protected $cacheKey; + + /** + * Initialize the simple cache + * + * Config is as following: + * memcache memcache instance + * cachekey the key in the cache to look at + * + * @param array $cfg Config array + */ + public function MemcacheSchemaCache($cfg) + { + $this->c = $cfg['c']; + $this->cacheKey = $cfg['cacheKey']; + } + + /** + * Return the schema object from the cache + * + * @return Net_LDAP2_Schema|Net_LDAP2_Error|false + */ + public function loadSchema() + { + return $this->c->get($this->cacheKey); + } + + /** + * Store a schema object in the cache + * + * This method will be called, if Net_LDAP2 has fetched a fresh + * schema object from LDAP and wants to init or refresh the cache. + * + * To invalidate the cache and cause Net_LDAP2 to refresh the cache, + * you can call this method with null or false as value. + * The next call to $ldap->schema() will then refresh the caches object. + * + * @param mixed $schema The object that should be cached + * @return true|Net_LDAP2_Error|false + */ + public function storeSchema($schema) { + return $this->c->set($this->cacheKey, $schema); + } +}