Completed
Pull Request — 2.x (#80)
by
unknown
02:00
created

HtpasswdAdapter   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 10
c 7
b 0
f 0
lcom 1
cbo 5
dl 0
loc 131
ccs 34
cts 34
cp 1
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getVerifier() 0 4 1
A __construct() 0 5 1
A login() 0 9 1
B fetchHashedPassword() 0 31 5
A verify() 0 6 2
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Auth\Adapter;
10
11
use Aura\Auth\Exception;
12
use Aura\Auth\Verifier\VerifierInterface;
13
14
/**
15
 *
16
 * Authenticate against a file generated by htpassword.
17
 *
18
 * Format for each line is "username:hashedpassword\n";
19
 *
20
 * Automatically checks against DES, SHA, and apr1-MD5.
21
 *
22
 * SECURITY NOTE: Default DES encryption will only check up to the first
23
 * 8 characters of a password; chars after 8 are ignored.  This means
24
 * that if the real password is "atechars", the word "atecharsnine" would
25
 * be valid.  This is bad.  As a workaround, if the password provided by
26
 * the user is longer than 8 characters, and DES encryption is being
27
 * used, this class will *not* validate it.
28
 *
29
 * @package Aura.Auth
30
 *
31
 */
32
class HtpasswdAdapter extends AbstractAdapter
33
{
34
    /**
35
     *
36
     * The httpasswd credential file.
37
     *
38
     * @var string
39
     *
40
     */
41
    protected $file;
42
43
    /**
44
     *
45
     * A verifier for passwords.
46
     *
47
     * @var VerifierInterface
48
     *
49
     */
50
    protected $verifier;
51
52
    /**
53
     *
54
     * Constructor.
55
     *
56
     * @param string $file The htpasswd file path.
57
     *
58
     * @param VerifierInterface $verifier
59
     *
60
     * @return null
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
61
     */
62 8
    public function __construct($file, VerifierInterface $verifier)
63
    {
64 8
        $this->file = $file;
65 8
        $this->verifier = $verifier;
66 8
    }
67
68
    /**
69
     *
70
     * Return object of type VerifierInterface
71
     *
72
     * @return VerifierInterface
73
     *
74
     */
75 1
    public function getVerifier()
76
    {
77 1
        return $this->verifier;
78
    }
79
80
    /**
81
     *
82
     * Verifies a set of credentials.
83
     *
84
     * @param array $input An array with keys 'username' and 'password'.
85
     *
86
     * @return array An array of login data.
87
     *
88
     */
89 6
    public function login(array $input)
90
    {
91 6
        $this->checkInput($input);
92 4
        $username = $input['username'];
93 4
        $password = $input['password'];
94 4
        $hashvalue = $this->fetchHashedPassword($username);
95 2
        $this->verify($password, $hashvalue);
96 1
        return array($username, array());
97
    }
98
99
    /**
100
     *
101
     * Reads the hashed password for a username from the htpasswd file.
102
     *
103
     * @param string $username The username to find in the htpasswd file.
104
     *
105
     * @return string
106
     *
107
     * @throws Exception\UsernameNotFound when the username is not found in the
108
     * htpasswd file.
109
     *
110
     */
111 4
    protected function fetchHashedPassword($username)
112
    {
113
        // force the full, real path to the file
114 4
        $real = realpath($this->file);
115 4
        if (! $real) {
116 1
            throw new Exception\FileNotReadable($this->file);
117
        }
118
119
        // find the user's line in the file
120 3
        $fp = fopen($real, 'r');
121 3
        $len = strlen($username) + 1;
122 3
        $hashvalue = false;
123 3
        while ($line = fgets($fp)) {
124 3
            if (substr($line, 0, $len) == "{$username}:") {
125
                // found the line, leave the loop
126 2
                $tmp = explode(':', trim($line));
127 2
                $hashvalue = $tmp[1];
128 2
                break;
129
            }
130 1
        }
131
132
        // close the file
133 3
        fclose($fp);
134
135
        // did we find the encrypted password for the username?
136 3
        if ($hashvalue) {
137 2
            return $hashvalue;
138
        }
139
140 1
        throw new Exception\UsernameNotFound;
141
    }
142
143
    /**
144
     *
145
     * Verifies the input password against the hashed password.
146
     *
147
     * @param string $password The input password.
148
     *
149
     * @param string $hashvalue The hashed password in htpasswd.
150
     *
151
     * @return null
152
     *
153
     * @throws Exception\PasswordIncorrect on failed verification.
154
     *
155
     */
156 2
    protected function verify($password, $hashvalue)
157
    {
158 2
        if (! $this->verifier->verify($password, $hashvalue)) {
159 1
            throw new Exception\PasswordIncorrect;
160
        }
161 1
    }
162
}
163