diff --git a/EVENTS.txt b/EVENTS.txt index 7be611c710..c52f0e3128 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -479,3 +479,7 @@ CheckPassword: Check a username/password - $nickname: The nickname to check - $password: The password to check - &$authenticated: set to true to indicate authentication succeeded. + +AutoRegister: Register a new user with the given nickname. Should insert a new User and Profile into the database. +- $nickname: The nickname to register + diff --git a/index.php b/index.php index 577b491ed0..b1e4f651e4 100644 --- a/index.php +++ b/index.php @@ -68,6 +68,7 @@ function getPath($req) */ function handleError($error) { +//error_log(print_r($error,1)); if ($error->getCode() == DB_DATAOBJECT_ERROR_NODATA) { return; } diff --git a/lib/util.php b/lib/util.php index 46aa7f9012..a4865c46c1 100644 --- a/lib/util.php +++ b/lib/util.php @@ -119,22 +119,41 @@ function common_munge_password($password, $id) // check if a username exists and has matching password function common_check_user($nickname, $password) { - // NEVER allow blank passwords, even if they match the DB - if (mb_strlen($password) == 0) { - return false; - } + $authenticated = false; + $eventResult = Event::handle('CheckPassword', array($nickname, $password, &$authenticated)); $user = User::staticGet('nickname', $nickname); if (is_null($user) || $user === false) { - return false; + //user does not exist + if($authenticated){ + //a handler said these are valid credentials, so see if a plugin wants to auto register the user + if(Event::handle('AutoRegister', array($nickname))){ + //no handler registered the user + return false; + }else{ + $user = User::staticGet('nickname', $nickname); + if (is_null($user) || $user === false) { + common_log(LOG_WARNING, "A plugin handled the AutoRegister event, but did not actually register the user, nickname: $nickname"); + return false; + }else{ + return $user; + } + } + }else{ + //no handler indicated the credentials were valid, and we know their not valid because the user isn't in the database + return false; + } } else { - $authenticated = false; - Event::handle('CheckPassword', array($nickname, $password, &$authenticated)); - if(! $authenticated){ - //no handler asserted the user, so check ourselves - if (0 == strcmp(common_munge_password($password, $user->id), - $user->password)) { - //internal checking passed - $authenticated = true; + if($eventResult && ! $authenticated){ + //no handler was authoritative + if (mb_strlen($password) == 0) { + // NEVER allow blank passwords, even if they match the DB + return false; + }else{ + if (0 == strcmp(common_munge_password($password, $user->id), + $user->password)) { + //internal checking passed + $authenticated = true; + } } } if($authenticated){ diff --git a/plugins/Ldap/LdapPlugin.php b/plugins/Ldap/LdapPlugin.php index ec2b7977da..8c2d45b859 100644 --- a/plugins/Ldap/LdapPlugin.php +++ b/plugins/Ldap/LdapPlugin.php @@ -46,6 +46,43 @@ class LdapPlugin extends Plugin { if(ldap_check_password($nickname, $password)){ $authenticated = true; + //stop handling of other events, because we have an answer + return false; + } + if(common_config('ldap','authoritative')){ + //a false return stops handler processing + return false; + } + } + + function onAutoRegister($nickname) + { + $user = User::staticGet('nickname', $nickname); + if (! is_null($user) && $user !== false) { + common_log(LOG_WARNING, "An attempt was made to autoregister an existing user with nickname: $nickname"); + return; + } + + $attributes=array(); + $config_attributes = array('nickname','email','fullname','homepage','location'); + foreach($config_attributes as $config_attribute){ + $value = common_config('ldap', $config_attribute.'_attribute'); + if($value!==false){ + array_push($attributes,$value); + } + } + $entry = ldap_get_user($nickname,$attributes); + if($entry){ + $registration_data = array(); + foreach($config_attributes as $config_attribute){ + $value = common_config('ldap', $config_attribute.'_attribute'); + if($value!==false){ + $registration_data[$config_attribute]=$entry->getValue($value,'single'); + } + } + //error_log(print_r($registration_data,1)); + $user = User::register($registration_data); + //prevent other handlers from running, as we have registered the user return false; } } diff --git a/plugins/Ldap/README b/plugins/Ldap/README index 8a5095a5df..617738e0ba 100644 --- a/plugins/Ldap/README +++ b/plugins/Ldap/README @@ -11,6 +11,13 @@ $config['ldap']['basedn'] $config['ldap']['host'] $config['ldap']['nickname_attribute'] Set this to the name of the ldap attribute that holds the username. For example, on Microsoft's Active Directory, this should be set to 'sAMAccountName' +$config['ldap']['nickname_email'] Set this to the name of the ldap attribute that holds the user's email address. For example, on Microsoft's Active Directory, this should be set to 'mail' +$config['ldap']['nickname_fullname'] Set this to the name of the ldap attribute that holds the user's full name. For example, on Microsoft's Active Directory, this should be set to 'displayName' +$config['ldap']['nickname_homepage'] Set this to the name of the ldap attribute that holds the the url of the user's home page. +$config['ldap']['nickname_location'] Set this to the name of the ldap attribute that holds the user's location. + +$config['ldap']['authoritative'] Set to true if LDAP's responses are authoritative (meaning if LDAP fails, do check the any other plugins or the internal password database) +$config['ldap']['autoregister'] Set to true if users should be automatically created when they attempt to login Finally, add "addPlugin('ldap');" to the bottom of your config.php diff --git a/plugins/Ldap/ldap.php b/plugins/Ldap/ldap.php index fcb84610a6..d92a058fb9 100644 --- a/plugins/Ldap/ldap.php +++ b/plugins/Ldap/ldap.php @@ -38,19 +38,20 @@ function ldap_get_config(){ function ldap_get_connection($config = null){ if($config == null){ - static $ldap = null; - if($ldap!=null){ - return $ldap; - } $config = ldap_get_config(); } - $ldap = Net_LDAP2::connect($config); - if (PEAR::isError($ldap)) { - common_log(LOG_WARNING, 'Could not connect to LDAP server: '.$ldap->getMessage()); + + //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)) { + common_log(LOG_WARNING, 'Could not connect to LDAP server: '.$err->getMessage()); return false; - }else{ - return $ldap; } + return $ldap; } function ldap_check_password($username, $password){ @@ -58,12 +59,12 @@ function ldap_check_password($username, $password){ if(!$ldap){ return false; } - $dn = ldap_get_user_dn($username); - if(!$dn){ + $entry = ldap_get_user($username); + if(!$entry){ return false; }else{ $config = ldap_get_config(); - $config['binddn']=$dn; + $config['binddn']=$entry->dn(); $config['bindpw']=$password; if(ldap_get_connection($config)){ return true; @@ -74,17 +75,18 @@ function ldap_check_password($username, $password){ } /** - * get an LDAP user's DN given the user's username + * 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_dn($username){ +function ldap_get_user($username,$attributes=array()){ $ldap = ldap_get_connection(); $filter = Net_LDAP2_Filter::create(common_config('ldap','nickname_attribute'), 'equals', $username); $options = array( 'scope' => 'sub', - 'attributes' => array() + 'attributes' => $attributes ); $search = $ldap->search(null,$filter,$options); @@ -97,7 +99,7 @@ function ldap_get_user_dn($username){ return false; }else if($search->count()==1){ $entry = $search->shiftEntry(); - return $entry->dn(); + return $entry; }else{ common_log(LOG_WARNING, 'Found ' . $search->count() . ' ldap user with the username: ' . $username); return false;