blob: a97a7ef2e41b69818bd00edb63993b2d83441156 [file] [log] [blame]
<?php
/**
* File containing the ezcAuthenticationHtpasswdFilter 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
* under the License.
*
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @filesource
* @package Authentication
* @version //autogen//
*/
/**
* Filter to authenticate against an Unix htpasswd file.
*
* It supports files created with the htpasswd command options
* -m (MD5 encryption - different than the PHP md5() function)
* -d (CRYPT encryption)
* -s (SHA encryption)
* -p (plain text)
*
* The encryption used for the password field in the file will be detected
* automatically.
*
* The password property can be specified as plain text or in encrypted form,
* depending on the option 'plain' in the ezcAuthenticationHtpasswdOptions object
* used as options.
*
* Example:
* <code>
* $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal', 'b1b3773a05c0ed0176787a4f1574ff0075f7521e' );
* $authentication = new ezcAuthentication( $credentials );
* $authentication->session = new ezcAuthenticationSession();
* $authentication->addFilter( new ezcAuthenticationHtpasswdFilter( '/etc/htpasswd' ) );
* // add other filters if needed
* if ( !$authentication->run() )
* {
* // authentication did not succeed, so inform the user
* $status = $authentication->getStatus();
* $err = array(
* 'ezcAuthenticationHtpasswdFilter' => array(
* ezcAuthenticationHtpasswdFilter::STATUS_USERNAME_INCORRECT => 'Incorrect username',
* ezcAuthenticationHtpasswdFilter::STATUS_PASSWORD_INCORRECT => 'Incorrect password'
* )
* );
* foreach ( $status as $line )
* {
* list( $key, $value ) = each( $line );
* echo $err[$key][$value] . "\n";
* }
* }
* else
* {
* // authentication succeeded, so allow the user to see his content
* }
* </code>
*
* @property string $file
* The path and file name of the htpasswd file to use.
*
* @package Authentication
* @version //autogen//
* @mainclass
*/
class ezcAuthenticationHtpasswdFilter extends ezcAuthenticationFilter
{
/**
* Username is not found in the htpasswd file.
*/
const STATUS_USERNAME_INCORRECT = 1;
/**
* Password is incorrect.
*/
const STATUS_PASSWORD_INCORRECT = 2;
/**
* Holds the properties of this class.
*
* @var array(string=>mixed)
*/
private $properties = array();
/**
* Creates a new object of this class.
*
* @throws ezcBaseValueException
* if the value provided is not correct for the property $file
* @throws ezcBaseFileNotFoundException
* if $file does not exist
* @throws ezcBaseFilePermissionException
* if $file cannot be opened for reading
* @param string $file The path and file name of the htpasswd file to use
* @param ezcAuthenticationHtpasswdOptions $options Options for this class
*/
public function __construct( $file, ezcAuthenticationHtpasswdOptions $options = null )
{
$this->file = $file;
$this->options = ( $options === null ) ? new ezcAuthenticationHtpasswdOptions() : $options;
}
/**
* 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
* @throws ezcBaseFileNotFoundException
* if the $value file does not exist
* @throws ezcBaseFilePermissionException
* if the $value file cannot be opened for reading
* @param string $name The name of the property to set
* @param mixed $value The new value of the property
* @ignore
*/
public function __set( $name, $value )
{
switch ( $name )
{
case 'file':
if ( !is_string( $value ) )
{
throw new ezcBaseValueException( $name, $value, 'string' );
}
if ( !file_exists( $value ) )
{
throw new ezcBaseFileNotFoundException( $value );
}
if ( !is_readable( $value ) )
{
throw new ezcBaseFilePermissionException( $value, ezcBaseFileException::READ );
}
$this->properties[$name] = $value;
break;
default:
throw new ezcBasePropertyNotFoundException( $name );
}
}
/**
* 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
* @return mixed
* @ignore
*/
public function __get( $name )
{
switch ( $name )
{
case 'file':
return $this->properties[$name];
default:
throw new ezcBasePropertyNotFoundException( $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
* @return bool
* @ignore
*/
public function __isset( $name )
{
switch ( $name )
{
case 'file':
return isset( $this->properties[$name] );
default:
return false;
}
}
/**
* Runs the filter and returns a status code when finished.
*
* @param ezcAuthenticationPasswordCredentials $credentials Authentication credentials
* @return int
*/
public function run( $credentials )
{
$fh = fopen( $this->file, 'r' );
$found = false;
while ( $line = fgets( $fh ) )
{
if ( substr( $line, 0, strlen( $credentials->id ) + 1 ) === $credentials->id . ':' )
{
$found = true;
break;
}
}
fclose( $fh );
if ( $found )
{
$parts = explode( ':', $line );
$hashFromFile = trim( $parts[1] );
if ( substr( $hashFromFile, 0, 6 ) === '$apr1$' )
{
$password = ( $this->options->plain ) ? ezcAuthenticationMath::apr1( $credentials->password, $hashFromFile ) :
'$apr1$' . $credentials->password;
}
elseif ( substr( $hashFromFile, 0, 5 ) === '{SHA}' )
{
$password = ( $this->options->plain ) ? '{SHA}' . base64_encode( pack( 'H40', sha1( $credentials->password ) ) ) :
'{SHA}' . $credentials->password;
}
else
{
$password = ( $this->options->plain ) ? crypt( $credentials->password, $hashFromFile ) :
$credentials->password;
}
if ( $password === $hashFromFile )
{
return self::STATUS_OK;
}
else
{
return self::STATUS_PASSWORD_INCORRECT;
}
}
return self::STATUS_USERNAME_INCORRECT;
}
}
?>