Apache Zeta Components Manual :: File Source for ldap_filter.php
Source for file ldap_filter.php
Documentation is available at ldap_filter.php
* File containing the ezcAuthenticationLdapFilter class.
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @package Authentication
* Filter to authenticate against an LDAP directory.
* This filter depends on the PHP ldap extension. If this extension is not
* installed then the constructor will throw an ezcExtensionNotFoundException.
* RFC: {@link http://www.faqs.org/rfcs/rfc4510.html}
* $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal', 'qwerty' );
* $ldap = new ezcAuthenticationLdapInfo( 'localhost', 'uid=%id%', 'dc=example,dc=com', 389 );
* $authentication = new ezcAuthentication( $credentials );
* $authentication->addFilter( new ezcAuthenticationLdapFilter( $ldap ) );
* // add more filters if needed
* if ( !$authentication->run() )
* // authentication did not succeed, so inform the user
* $status = $authentication->getStatus();
* 'ezcAuthenticationLdapFilter' => array(
* ezcAuthenticationLdapFilter::STATUS_USERNAME_INCORRECT => 'Incorrect username',
* ezcAuthenticationLdapFilter::STATUS_PASSWORD_INCORRECT => 'Incorrect password'
* foreach ( $status as $line )
* list( $key, $value ) = each( $line );
* echo $err[$key][$value] . "\n";
* // authentication succeeded, so allow the user to see his content
* Extra data can be fetched from the LDAP server during the authentication
* process, by registering the data to be fetched before calling run(). Example:
* // $filter is an ezcAuthenticationLdapFilter object
* $filter->registerFetchData( array( 'name', 'company', 'mobile' ) );
* $data = $filter->fetchData();
* The $data array will be something like:
* array( 'name' = > array( 'Dr. No' ),
* 'company' => array( 'SPECTRE' ),
* 'mobile' => array( '555-7732873' )
* @property ezcAuthenticationLdapInfo $ldap
* Structure which holds the LDAP server hostname, entry format and base,
* @package Authentication
* Username is not found in the database.
* Use plain-text password and no encryption for the connection (default).
* Use plain-text password and TLS connection.
* Holds the attributes which will be requested during the authentication
* Usually it has this structure:
* array( 'name', 'company', 'mobile' );
* Holds the extra data fetched during the authentication process.
* Usually it has this structure:
* array( 'name' = > array( 'Dr. No' ),
* 'company' => array( 'SPECTRE' ),
* 'mobile' => array( '555-7732873' )
* @var array(string=>mixed)
protected $data =
array();
* Holds the properties of this class.
* @var array(string=>mixed)
private $properties =
array();
* Creates a new object of this class.
* @throws ezcBaseExtensionNotFoundException
* if the PHP ldap extension is not installed
* @param ezcAuthenticationLdapInfo $ldap How to connect to LDAP
* @param ezcAuthenticationLdapOptions $options Options for this class
public function __construct( ezcAuthenticationLdapInfo $ldap, ezcAuthenticationLdapOptions $options =
null )
* Sets the property $name to $value.
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @throws ezcBaseValueException
* if $value is not correct for the property $name
* @param string $name The name of the property to set
* @param mixed $value The new value of the property
public function __set( $name, $value )
$this->properties[$name] =
$value;
* Returns the value of the property $name.
* @throws ezcBasePropertyNotFoundException
* if the property $name does not exist
* @param string $name The name of the property for which to return the value
public function __get( $name )
return $this->properties[$name];
* Returns true if the property $name is set, otherwise false.
* @param string $name The name of the property to test if it is set
public function __isset( $name )
return isset
( $this->properties[$name] );
* Runs the filter and returns a status code when finished.
* @throws ezcAuthenticationLdapException
* if the connecting and binding to the LDAP server could not be performed
* @param ezcAuthenticationPasswordCredentials $credentials Authentication credentials
public function run( $credentials )
$protocol =
'ldap://'; // 'ldaps://' will be implemented later (if ever, as TLS is better)
// null, false or empty string passwords are not accepted, as on some servers
// they could cause the LDAP binding to succeed
if ( empty( $credentials->password ) )
return self::STATUS_PASSWORD_INCORRECT;
$connection =
$this->ldapConnect( $this->ldap->host, $this->ldap->port );
// OpenLDAP 2.x.x will not throw an exception because $connection is always a resource
// without using version 3, TLS and other stuff won't work
ldap_set_option( $connection, LDAP_OPT_PROTOCOL_VERSION, 3 );
ldap_set_option( $connection, LDAP_OPT_REFERRALS, 0 );
// try to use a TLS connection
if ( $this->options->protocol ===
self::PROTOCOL_TLS ||
$this->ldap->protocol ===
self::PROTOCOL_TLS )
// using TLS, so continue
// bind anonymously to see if username exists in the directory
if ( @ldap_bind( $connection ) )
$search =
@ldap_search( $connection, $this->ldap->base, str_replace( '%id%', $credentials->id, $this->ldap->format ), $this->requestedData );
if ( !$search ||
ldap_count_entries( $connection, $search ) ===
0 )
ldap_close( $connection );
return self::STATUS_USERNAME_INCORRECT;
// username exists, so get dn for it
$entry =
ldap_first_entry( $connection, $search );
$entryDN =
ldap_get_dn( $connection, $entry );
// check if we can bind with the provided password
if ( @ldap_bind( $connection, $entryDN, $credentials->password ) )
// retrieve extra authentication data
$attributes =
ldap_get_attributes( $connection, $entry );
// ignore case of $attribute
if ( isset
( $attributes[$attribute] )
for ( $i =
0; $i <
$attributes[$attribute]['count']; $i++
)
$this->data[$attribute][] =
$attributes[$attribute][$i];
// DN is a 'special' attribute and is not returned by ldap_get_attributes()
// An entry can only have one DN
$this->data[$attribute] =
$entryDN;
ldap_close( $connection );
// bind failed, so something must be wrong (connection error or password incorrect)
$err =
ldap_errno( $connection );
ldap_close( $connection );
// error codes: 0 = success, 49 = invalidCredentials, 50 = insufficientAccessRights
// so if any other codes appear it must mean that we could not connect to
if ( $err !==
0 &&
$err !==
49 &&
$err !==
50 )
throw
new ezcAuthenticationLdapException( "Could not connect to host '{$protocol}{$this->ldap->host}:{
$this->ldap->port}'
", $err, ldap_err2str( $err ) );
return self::STATUS_PASSWORD_INCORRECT;
* Wraps around the ldap_connect() function.
* Returns the connection as a resource if it was successful.
* @param string $host The LDAP hostname
* @param int $port The LDAP port to connect to $host, default 389
return ldap_connect( $host, $port );
* Wraps around the ldap_start_tls() function.
* Returns true if it was possible to start a TLS connection on the provided
* @param mixed $connection An established LDAP connection
return @ldap_start_tls( $connection );
* Registers which extra data to fetch during the authentication process.
* The input $data is an array of attributes to request, for example:
* array( 'name', 'company', 'mobile' );
* @param array(string) $data A list of attributes to fetch during authentication
* Returns the extra data fetched during the authentication process.
* The return is something like:
* array( 'name' = > array( 'Dr. No' ),
* 'company' => array( 'SPECTRE' ),
* 'mobile' => array( '555-7732873' )
* @return array(string=>mixed)