Skip to content

Commit

Permalink
Merge pull request #175 from lemeur/master
Browse files Browse the repository at this point in the history
Fixed #8369: Added search-and-bind feature for the core LDAP authentication plugin (Thibault Le Meur)
  • Loading branch information
mennodekker committed Mar 12, 2014
2 parents e944239 + d13e837 commit 55034bf
Showing 1 changed file with 131 additions and 44 deletions.
175 changes: 131 additions & 44 deletions application/core/plugins/AuthLDAP/AuthLDAP.php
Expand Up @@ -2,48 +2,74 @@
class AuthLDAP extends AuthPluginBase
{
protected $storage = 'DbStorage';

static protected $description = 'Core: Basic LDAP authentication';
static protected $name = 'LDAP';

/**
* Can we autocreate users? For the moment this is disabled, will be moved
* to a setting when we have more robust user creation system.
*
* @var boolean
*/
protected $autoCreate = false;

protected $settings = array(
'server' => array(
'type' => 'string',
'label' => 'Ldap server e.g. ldap://ldap.mydomain.com'
),
'label' => 'Ldap server e.g. ldap://ldap.mydomain.com or ldaps://ldap.mydomain.com'
),
'ldapport' => array(
'type' => 'string',
'label' => 'Port number (default when omitted is 389)'
),
),
'ldapversion' => array(
'type' => 'string',
'label' => 'LDAP version (LDAPv2 = 2), e.g. 3'
),
),
'ldapmode' => array(
'type' => 'select',
'label' => 'Select how to perform authentication.',
'options' => array("simplebind" => "Simple bind", "searchandbind" => "Search and bind"),
'default' => "simplebind",
),
'userprefix' => array(
'type' => 'string',
'label' => 'Username prefix cn= or uid='
),
'label' => '[Simple bind] Username prefix cn= or uid=',
),
'domainsuffix' => array(
'type' => 'string',
'label' => 'Username suffix e.g. @mydomain.com or remaining part of ldap query'
),
'type' => 'string',
'label' => '[Simple bind] Username suffix e.g. @mydomain.com or remaining part of ldap query'
),
'searchuserattribute' => array(
'type' => 'string',
'label' => '[Search and bind] attribute to compare to the given login can be uid, cn, mail, ...'
),
'usersearchbase' => array(
'type' => 'string',
'label' => '[Search and bind] base DN for the user search operation'
),
'extrauserfilter' => array(
'type' => 'string',
'label' => '[Search and bind](optional) Extra LDAP filter to be ANDed to the basic (searchuserattribute=username) filter. Don\'t forget the outmost enclosing parentheses'
),
'binddn' => array(
'type' => 'string',
'label' => '[Search and bind](optional) DN of the search-DN-user used to search for the user\'s DN. An anonymous bind is performed if empty.'
),
'bindpwd' => array(
'type' => 'string',
'label' => '[Search and bind](optional) Password used to bind the search-DN-user unless anonymous bind is used.'
),
'is_default' => array(
'type' => 'checkbox',
'label' => 'Check to make default authentication method'
)
'type' => 'checkbox',
'label' => 'Check to make default authentication method'
)
);

public function __construct(PluginManager $manager, $id) {
parent::__construct($manager, $id);

/**
* Here you should handle subscribing to the events your plugin will handle
*/
Expand All @@ -52,22 +78,22 @@ public function __construct(PluginManager $manager, $id) {
$this->subscribe('afterLoginFormSubmit');
$this->subscribe('newUserSession');
}

public function beforeLogin()
{
if ($this->get('is_default', null, null, false) == true) {
// This is configured to be the default login method
$this->getEvent()->set('default', get_class($this));
}
}

public function newLoginForm()
{
$this->getEvent()->getContent($this)
->addContent(CHtml::tag('li', array(), "<label for='user'>" . gT("Username") . "</label><input name='user' id='user' type='text' size='40' maxlength='40' value='' />"))
->addContent(CHtml::tag('li', array(), "<label for='password'>" . gT("Password") . "</label><input name='password' id='password' type='password' size='40' maxlength='40' value='' />"));
->addContent(CHtml::tag('li', array(), "<label for='user'>" . gT("Username") . "</label><input name='user' id='user' type='text' size='40' maxlength='40' value='' />"))
->addContent(CHtml::tag('li', array(), "<label for='password'>" . gT("Password") . "</label><input name='password' id='password' type='password' size='40' maxlength='40' value='' />"));
}

public function afterLoginFormSubmit()
{
// Here we handle post data
Expand All @@ -77,29 +103,37 @@ public function afterLoginFormSubmit()
$this->setPassword($request->getPost('password'));
}
}

public function newUserSession()
{
// Here we do the actual authentication
$username = $this->getUsername();
$password = $this->getPassword();

$user = $this->api->getUserByName($username);

if ($user === null && $this->autoCreate === false)
{
// If the user doesnt exist ín the LS database, he can not login
$this->setAuthFailure(self::ERROR_USERNAME_INVALID);
return;
}

// Get configuration settings:
$ldapserver = $this->get('server');
$ldapport = $this->get('ldapport');
$ldapver = $this->get('ldapversion');
$suffix = $this->get('domainsuffix');
$prefix = $this->get('userprefix');

$ldapserver = $this->get('server');
$ldapport = $this->get('ldapport');
$ldapver = $this->get('ldapversion');
$ldapmode = $this->get('ldapmode');
$suffix = $this->get('domainsuffix');
$prefix = $this->get('userprefix');
$searchuserattribute = $this->get('searchuserattribute');
$extrauserfilter = $this->get('extrauserfilter');
$usersearchbase = $this->get('usersearchbase');
$binddn = $this->get('binddn');
$bindpwd = $this->get('bindpwd');



if (empty($ldapport)) {
$ldapport = 389;
}
Expand All @@ -110,7 +144,7 @@ public function newUserSession()
$this->setAuthFailure(1, gT('Could not connect to LDAP server.'));
return;
}

// using LDAP version
if ($ldapver === null)
{
Expand All @@ -119,39 +153,92 @@ public function newUserSession()
}
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, $ldapver);

// binding to ldap server
$ldapbind = ldap_bind($ldapconn, $prefix . $username . $suffix, $password);
// verify binding
if (empty($ldapmode) || $ldapmode=='simplebind')
{
// in simple bind mode we know how to construct the userDN from the username
$ldapbind = ldap_bind($ldapconn, $prefix . $username . $suffix, $password);
}
else
{
// in search and bind mode we first do a LDAP search from the username given
// to foind the userDN and then we procced to the bind operation
if (empty($binddn))
{
// There is no account defined to do the LDAP search,
// let's use anonymous bind instead
$ldapbindsearch = ldap_bind($ldapconn);
}
else
{
// An account is defined to do the LDAP search, let's use it
$ldapbindsearch = ldap_bind($ldapconn, $binddn, $bindpwd);
}
if (!$ldapbindsearch) {
$this->setAuthFailure(100, ldap_error($ldapconn));
ldap_close($ldapconn); // all done? close connection
return;
}
// Now prepare the search fitler
if ( $extrauserfilter != "")
{
$usersearchfilter = "(&($searchuserattribute=$username)$extrauserfilter)";
}
else
{
$usersearchfilter = "($searchuserattribute=$username)";
}
// Search for the user
$dnsearchres = ldap_search($ldapconn, $usersearchbase, $usersearchfilter, array($searchuserattribute));
$rescount=ldap_count_entries($ldapconn,$dnsearchres);
if ($rescount == 1)
{
$userentry=ldap_get_entries($ldapconn, $dnsearchres);
$userdn = $userentry[0]["dn"];
}
else
{
// if no entry or more than one entry returned
// then deny authentication
$this->setAuthFailure(100, ldap_error($ldapconn));
ldap_close($ldapconn); // all done? close connection
return;
}

// binding to ldap server with teh userDN and privided credentials
$ldapbind = ldap_bind($ldapconn, $userdn, $password);
}

// verify user binding
if (!$ldapbind) {
$this->setAuthFailure(100, ldap_error($ldapconn));
ldap_close($ldapconn); // all done? close connection
return;
}

// Authentication was successful, now see if we have a user or that we should create one
if (is_null($user)) {
if (is_null($user)) {
if ($this->autoCreate === true) {
/*
* Dispatch the newUserLogin event, and hope that after this we can find the user
* this allows users to create their own plugin for handling the user creation
* we will need more methods to pass username, rdn and ldap connection.
*/
$this->pluginManager->dispatchEvent(new PluginEvent('newUserLogin', $this));
// Check ourselves, we do not want fake resonses from a plugin

// Check ourselves, we do not want fake responses from a plugin
$user = $this->api->getUserByName($username);
}

if (is_null($user)) {
$this->setAuthFailure(self::ERROR_USERNAME_INVALID);
ldap_close($ldapconn); // all done? close connection
return;
}
}

ldap_close($ldapconn); // all done? close connection

// If we made it here, authentication was a success and we do have a valid user
$this->setAuthSuccess($user);
}
}
}

0 comments on commit 55034bf

Please sign in to comment.