Apache Zeta Components Manual :: File Source for ldap_filter.php

Source for file ldap_filter.php

Documentation is available at ldap_filter.php

  1. <?php
  2. /**
  3.  * File containing the ezcAuthenticationLdapFilter class.
  4.  *
  5.  * Licensed to the Apache Software Foundation (ASF) under one
  6.  * or more contributor license agreements.  See the NOTICE file
  7.  * distributed with this work for additional information
  8.  * regarding copyright ownership.  The ASF licenses this file
  9.  * to you under the Apache License, Version 2.0 (the
  10.  * "License"); you may not use this file except in compliance
  11.  * with the License.  You may obtain a copy of the License at
  12.  * 
  13.  *   http://www.apache.org/licenses/LICENSE-2.0
  14.  * 
  15.  * Unless required by applicable law or agreed to in writing,
  16.  * software distributed under the License is distributed on an
  17.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18.  * KIND, either express or implied.  See the License for the
  19.  * specific language governing permissions and limitations
  20.  * under the License.
  21.  *
  22.  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  23.  * @filesource
  24.  * @package Authentication
  25.  * @version //autogen//
  26.  */
  27.  
  28. /**
  29.  * Filter to authenticate against an LDAP directory.
  30.  *
  31.  * This filter depends on the PHP ldap extension. If this extension is not
  32.  * installed then the constructor will throw an ezcExtensionNotFoundException.
  33.  *
  34.  * RFC: {@link http://www.faqs.org/rfcs/rfc4510.html}
  35.  *
  36.  * Example:
  37.  * <code>
  38.  * $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal', 'qwerty' );
  39.  * $ldap = new ezcAuthenticationLdapInfo( 'localhost', 'uid=%id%', 'dc=example,dc=com', 389 );
  40.  * $authentication = new ezcAuthentication( $credentials );
  41.  * $authentication->addFilter( new ezcAuthenticationLdapFilter( $ldap ) );
  42.  * // add more filters if needed
  43.  * if ( !$authentication->run() )
  44.  * {
  45.  *     // authentication did not succeed, so inform the user
  46.  *     $status = $authentication->getStatus();
  47.  *     $err = array(
  48.  *             'ezcAuthenticationLdapFilter' => array(
  49.  *                 ezcAuthenticationLdapFilter::STATUS_USERNAME_INCORRECT => 'Incorrect username',
  50.  *                 ezcAuthenticationLdapFilter::STATUS_PASSWORD_INCORRECT => 'Incorrect password'
  51.  *                 )
  52.  *             );
  53.  *     foreach ( $status as $line )
  54.  *     {
  55.  *         list( $key, $value ) = each( $line );
  56.  *         echo $err[$key][$value] . "\n";
  57.  *     }
  58.  * }
  59.  * else
  60.  * {
  61.  *     // authentication succeeded, so allow the user to see his content
  62.  * }
  63.  * </code>
  64.  *
  65.  * Extra data can be fetched from the LDAP server during the authentication
  66.  * process, by registering the data to be fetched before calling run(). Example:
  67.  * <code>
  68.  * // $filter is an ezcAuthenticationLdapFilter object
  69.  * $filter->registerFetchData( array( 'name', 'company', 'mobile' ) );
  70.  *
  71.  * // after run()
  72.  * $data = $filter->fetchData();
  73.  * </code>
  74.  *
  75.  * The $data array will be something like:
  76.  * <code>
  77.  * array( 'name' = > array( 'Dr. No' ),
  78.  *        'company' => array( 'SPECTRE' ),
  79.  *        'mobile' => array( '555-7732873' )
  80.  *      );
  81.  * </code>
  82.  *
  83.  * @property ezcAuthenticationLdapInfo $ldap 
  84.  *            Structure which holds the LDAP server hostname, entry format and base,
  85.  *            and port.
  86.  *
  87.  * @package Authentication
  88.  * @version //autogen//
  89.  * @mainclass
  90.  */
  91. {
  92.     /**
  93.      * Username is not found in the database.
  94.      */
  95.     const STATUS_USERNAME_INCORRECT = 1;
  96.  
  97.     /**
  98.      * Password is incorrect.
  99.      */
  100.     const STATUS_PASSWORD_INCORRECT = 2;
  101.  
  102.     /**
  103.      * Use plain-text password and no encryption for the connection (default).
  104.      */
  105.     const PROTOCOL_PLAIN = 1;
  106.  
  107.     /**
  108.      * Use plain-text password and TLS connection.
  109.      */
  110.     const PROTOCOL_TLS = 2;
  111.  
  112.     /**
  113.      * Holds the attributes which will be requested during the authentication
  114.      * process.
  115.      *
  116.      * Usually it has this structure:
  117.      * <code>
  118.      * array( 'name', 'company', 'mobile' );
  119.      * </code>
  120.      *
  121.      * @var array(string) 
  122.      */
  123.     protected $requestedData = array();
  124.  
  125.     /**
  126.      * Holds the extra data fetched during the authentication process.
  127.      *
  128.      * Usually it has this structure:
  129.      * <code>
  130.      * array( 'name' = > array( 'Dr. No' ),
  131.      *        'company' => array( 'SPECTRE' ),
  132.      *        'mobile' => array( '555-7732873' )
  133.      *      );
  134.      * </code>
  135.      *
  136.      * @var array(string=>mixed) 
  137.      */
  138.     protected $data = array();
  139.  
  140.     /**
  141.      * Holds the properties of this class.
  142.      *
  143.      * @var array(string=>mixed) 
  144.      */
  145.     private $properties array();
  146.  
  147.     /**
  148.      * Creates a new object of this class.
  149.      *
  150.      * @throws ezcBaseExtensionNotFoundException
  151.      *          if the PHP ldap extension is not installed
  152.      * @param ezcAuthenticationLdapInfo $ldap How to connect to LDAP
  153.      * @param ezcAuthenticationLdapOptions $options Options for this class
  154.      */
  155.     public function __constructezcAuthenticationLdapInfo $ldapezcAuthenticationLdapOptions $options null )
  156.     {
  157.         if !ezcBaseFeatures::hasExtensionSupport'ldap' ) )
  158.         {
  159.             throw new ezcBaseExtensionNotFoundException'ldap'null"PHP not configured with --with-ldap." );
  160.         }
  161.  
  162.         $this->ldap $ldap;
  163.         $this->options = $options === null new ezcAuthenticationLdapOptions($options;
  164.     }
  165.  
  166.     /**
  167.      * Sets the property $name to $value.
  168.      *
  169.      * @throws ezcBasePropertyNotFoundException
  170.      *          if the property $name does not exist
  171.      * @throws ezcBaseValueException
  172.      *          if $value is not correct for the property $name
  173.      * @param string $name The name of the property to set
  174.      * @param mixed $value The new value of the property
  175.      * @ignore
  176.      */
  177.     public function __set$name$value )
  178.     {
  179.         switch $name )
  180.         {
  181.             case 'ldap':
  182.                 if $value instanceof ezcAuthenticationLdapInfo )
  183.                 {
  184.                     $this->properties[$name$value;
  185.                 }
  186.                 else
  187.                 {
  188.                     throw new ezcBaseValueException$name$value'ezcAuthenticationLdapInfo' );
  189.                 }
  190.                 break;
  191.  
  192.             default:
  193.                 throw new ezcBasePropertyNotFoundException$name );
  194.         }
  195.     }
  196.  
  197.     /**
  198.      * Returns the value of the property $name.
  199.      *
  200.      * @throws ezcBasePropertyNotFoundException
  201.      *          if the property $name does not exist
  202.      * @param string $name The name of the property for which to return the value
  203.      * @return mixed 
  204.      * @ignore
  205.      */
  206.     public function __get$name )
  207.     {
  208.         switch $name )
  209.         {
  210.             case 'ldap':
  211.                 return $this->properties[$name];
  212.  
  213.             default:
  214.                 throw new ezcBasePropertyNotFoundException$name );
  215.         }
  216.     }
  217.  
  218.     /**
  219.      * Returns true if the property $name is set, otherwise false.
  220.      *
  221.      * @param string $name The name of the property to test if it is set
  222.      * @return bool 
  223.      * @ignore
  224.      */
  225.     public function __isset$name )
  226.     {
  227.         switch $name )
  228.         {
  229.             case 'ldap':
  230.                 return isset$this->properties[$name);
  231.  
  232.             default:
  233.                 return false;
  234.         }
  235.     }
  236.  
  237.     /**
  238.      * Runs the filter and returns a status code when finished.
  239.      *
  240.      * @throws ezcAuthenticationLdapException
  241.      *          if the connecting and binding to the LDAP server could not be performed
  242.      * @param ezcAuthenticationPasswordCredentials $credentials Authentication credentials
  243.      * @return int 
  244.      */
  245.     public function run$credentials )
  246.     {
  247.         $protocol 'ldap://'// 'ldaps://' will be implemented later (if ever, as TLS is better)
  248.  
  249.         // null, false or empty string passwords are not accepted, as on some servers
  250.         // they could cause the LDAP binding to succeed
  251.         if empty$credentials->password ) )
  252.         {
  253.             return self::STATUS_PASSWORD_INCORRECT;
  254.         }
  255.  
  256.         $connection $this->ldapConnect$this->ldap->host$this->ldap->port );
  257.         if !$connection )
  258.         {
  259.             // OpenLDAP 2.x.x will not throw an exception because $connection is always a resource
  260.             throw new ezcAuthenticationLdapException"Could not connect to host '{$protocol}{$this->ldap->host}:{$this->ldap->port}'.);
  261.         }
  262.  
  263.         // without using version 3, TLS and other stuff won't work
  264.         ldap_set_option$connectionLDAP_OPT_PROTOCOL_VERSION);
  265.         ldap_set_option$connectionLDAP_OPT_REFERRALS);
  266.  
  267.         // try to use a TLS connection
  268.         if $this->options->protocol === self::PROTOCOL_TLS || $this->ldap->protocol === self::PROTOCOL_TLS )
  269.         {
  270.             if $this->ldapStartTls$connection ) )
  271.             {
  272.                 // using TLS, so continue
  273.             }
  274.             else
  275.             {
  276.                 throw new ezcAuthenticationLdapException"Could not connect to host '{$protocol}{$this->ldap->host}:{$this->ldap->port}'.);
  277.             }
  278.         }
  279.  
  280.         // bind anonymously to see if username exists in the directory
  281.         if @ldap_bind$connection ) )
  282.         {
  283.             $search @ldap_search$connection$this->ldap->basestr_replace'%id%'$credentials->id$this->ldap->format )$this->requestedData );
  284.             if !$search || ldap_count_entries$connection$search === )
  285.             {
  286.                 ldap_close$connection );
  287.                 return self::STATUS_USERNAME_INCORRECT;
  288.             }
  289.  
  290.             // username exists, so get dn for it
  291.             $entry ldap_first_entry$connection$search );
  292.             $entryDN ldap_get_dn$connection$entry );
  293.  
  294.             // check if we can bind with the provided password
  295.             if @ldap_bind$connection$entryDN$credentials->password ) )
  296.             {
  297.                 // retrieve extra authentication data
  298.                 if count$this->requestedData )
  299.                 {
  300.                     $attributes ldap_get_attributes$connection$entry );
  301.  
  302.                     foreach $this->requestedData as $attribute )
  303.                     {
  304.                         // ignore case of $attribute
  305.                         if isset$attributes[$attribute)
  306.                              || isset$attributes[strtolower$attribute ))
  307.                            )
  308.                         {
  309.                             for $i 0$i $attributes[$attribute]['count']$i++ )
  310.                             {
  311.                                 $this->data[$attribute][$attributes[$attribute][$i];
  312.                             }
  313.                         }
  314.  
  315.                         // DN is a 'special' attribute and is not returned by ldap_get_attributes()
  316.                         if strtolower$attribute == 'dn' )
  317.                         {
  318.                             // An entry can only have one DN
  319.                             $this->data[$attribute$entryDN;
  320.                         }
  321.                     }
  322.                 }
  323.  
  324.                 ldap_close$connection );
  325.                 return self::STATUS_OK;
  326.             }
  327.         }
  328.  
  329.         // bind failed, so something must be wrong (connection error or password incorrect)
  330.         $err ldap_errno$connection );
  331.         ldap_close$connection );
  332.  
  333.         // error codes: 0 = success, 49 = invalidCredentials, 50 = insufficientAccessRights
  334.         // so if any other codes appear it must mean that we could not connect to
  335.         // the LDAP host
  336.         if $err !== && $err !== 49 && $err !== 50 )
  337.         {
  338.             throw new ezcAuthenticationLdapException"Could not connect to host '{$protocol}{$this->ldap->host}:{$this->ldap->port}'"$errldap_err2str$err ) );
  339.         }
  340.  
  341.         return self::STATUS_PASSWORD_INCORRECT;
  342.     }
  343.  
  344.     /**
  345.      * Wraps around the ldap_connect() function.
  346.      *
  347.      * Returns the connection as a resource if it was successful.
  348.      *
  349.      * @param string $host The LDAP hostname
  350.      * @param int $port The LDAP port to connect to $host, default 389
  351.      * @return mixed 
  352.      */
  353.     protected function ldapConnect$host$port 389 )
  354.     {
  355.         return ldap_connect$host$port );
  356.     }
  357.  
  358.     /**
  359.      * Wraps around the ldap_start_tls() function.
  360.      *
  361.      * Returns true if it was possible to start a TLS connection on the provided
  362.      * $connection.
  363.      *
  364.      * @param mixed $connection An established LDAP connection
  365.      * @return bool 
  366.      */
  367.     protected function ldapStartTls$connection )
  368.     {
  369.         return @ldap_start_tls$connection );
  370.     }
  371.  
  372.     /**
  373.      * Registers which extra data to fetch during the authentication process.
  374.      *
  375.      * The input $data is an array of attributes to request, for example:
  376.      * <code>
  377.      * array( 'name', 'company', 'mobile' );
  378.      * </code>
  379.      *
  380.      * @param array(string) $data A list of attributes to fetch during authentication
  381.      */
  382.     public function registerFetchDataarray $data array() )
  383.     {
  384.         $this->requestedData = $data;
  385.     }
  386.  
  387.     /**
  388.      * Returns the extra data fetched during the authentication process.
  389.      *
  390.      * The return is something like:
  391.      * <code>
  392.      * array( 'name' = > array( 'Dr. No' ),
  393.      *        'company' => array( 'SPECTRE' ),
  394.      *        'mobile' => array( '555-7732873' )
  395.      *      );
  396.      * </code>
  397.      *
  398.      * @return array(string=>mixed) 
  399.      */
  400.     public function fetchData()
  401.     {
  402.         return $this->data;
  403.     }
  404. }
  405. ?>
Documentation generated by phpDocumentor 1.4.3