3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Authentication\Adapter\Http
;
12 use Zend\Authentication\Result
as AuthResult
;
13 use Zend\Crypt\Password\Apache
as ApachePassword
;
14 use Zend\Stdlib\ErrorHandler
;
17 * Apache Authentication Resolver
19 * @see http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
21 class ApacheResolver
implements ResolverInterface
24 * Path to credentials file
31 * Apache password object
35 protected $apachePassword;
40 * @param string $path Complete filename where the credentials are stored
42 public function __construct($path = '')
45 $this->setFile($path);
50 * Set the path to the credentials file
53 * @return FileResolver Provides a fluent interface
54 * @throws Exception\InvalidArgumentException if path is not readable
56 public function setFile($path)
58 if (empty($path) ||
!is_readable($path)) {
59 throw new Exception\
InvalidArgumentException('Path not readable: ' . $path);
67 * Returns the path to the credentials file
71 public function getFile()
77 * Returns the Apache Password object
79 * @return ApachePassword
81 protected function getApachePassword()
83 if (empty($this->apachePassword
)) {
84 $this->apachePassword
= new ApachePassword();
86 return $this->apachePassword
;
94 * @param string $username Username
95 * @param string $realm Authentication Realm
96 * @param string $password The password to authenticate
98 * @throws Exception\ExceptionInterface
100 public function resolve($username, $realm, $password = null)
102 if (empty($username)) {
103 throw new Exception\
InvalidArgumentException('Username is required');
106 if (!ctype_print($username) ||
strpos($username, ':') !== false) {
107 throw new Exception\
InvalidArgumentException(
108 'Username must consist only of printable characters, excluding the colon'
112 if (!empty($realm) && (!ctype_print($realm) ||
strpos($realm, ':') !== false)) {
113 throw new Exception\
InvalidArgumentException(
114 'Realm must consist only of printable characters, excluding the colon'
118 if (empty($password)) {
119 throw new Exception\
InvalidArgumentException('Password is required');
122 // Open file, read through looking for matching credentials
123 ErrorHandler
::start(E_WARNING
);
124 $fp = fopen($this->file
, 'r');
125 $error = ErrorHandler
::stop();
127 throw new Exception\
RuntimeException('Unable to open password file: ' . $this->file
, 0, $error);
130 // No real validation is done on the contents of the password file. The
131 // assumption is that we trust the administrators to keep it secure.
132 while (($line = fgetcsv($fp, 512, ':')) !== false) {
133 if ($line[0] != $username) {
137 if (isset($line[2])) {
138 if ($line[1] == $realm) {
139 $matchedHash = $line[2];
145 $matchedHash = $line[1];
150 if (!isset($matchedHash)) {
151 return new AuthResult(AuthResult
::FAILURE_IDENTITY_NOT_FOUND
, null, array('Username not found in provided htpasswd file'));
154 // Plaintext password
155 if ($matchedHash === $password) {
156 return new AuthResult(AuthResult
::SUCCESS
, $username);
159 $apache = $this->getApachePassword();
160 $apache->setUserName($username);
161 if (!empty($realm)) {
162 $apache->setAuthName($realm);
165 if ($apache->verify($password, $matchedHash)) {
166 return new AuthResult(AuthResult
::SUCCESS
, $username);
169 return new AuthResult(AuthResult
::FAILURE_CREDENTIAL_INVALID
, null, array('Passwords did not match.'));