Completed
Branch master (495df4)
by Anton
01:49
created

Table::callVerifyFunction()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 2
nop 2
dl 0
loc 15
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Bluz PHP Team
4
 * @link      https://github.com/bluzphp/skeleton
5
 */
6
7
/**
8
 * @namespace
9
 */
10
11
namespace Application\Auth;
12
13
use Application\Exception;
14
use Application\Users;
15
use Bluz\Application;
16
use Bluz\Auth\AbstractTable;
17
use Bluz\Auth\AuthException;
18
use Bluz\Proxy\Auth;
19
use Bluz\Proxy\Config;
20
use Bluz\Proxy\Response;
21
22
/**
23
 * Auth Table
24
 *
25
 * @package  Application\Auth
26
 *
27
 * @method   static Row findRow($primaryKey)
28
 * @method   static Row findRowWhere($whereList)
29
 *
30
 * @author   Anton Shevchuk
31
 * @created  12.07.11 15:28
32
 */
33
class Table extends AbstractTable
34
{
35
    /**
36
     * Time that the token remains valid
37
     */
38
    const TOKEN_EXPIRATION_TIME = 1800;
39
40
    /**
41
     * Authenticate user by login/pass
42
     *
43
     * @param string $username
44
     * @param string $password
45
     *
46
     * @throws AuthException
47
     * @throws Exception
48
     */
49 2
    public function authenticateEquals($username, $password)
50
    {
51 2
        $authRow = $this->checkEquals($username, $password);
52
53
        // get user profile
54 1
        if (!$user = Users\Table::findRow($authRow->userId)) {
55
            throw new Exception("User is undefined in system");
56
        }
57
58
        // try to login
59 1
        $user->tryLogin();
60 1
    }
61
62
    /**
63
     * Check user by login/pass
64
     *
65
     * @param string $username
66
     * @param string $password
67
     *
68
     * @throws AuthException
69
     * @return Row
70
     */
71 4
    public function checkEquals($username, $password)
72
    {
73 4
        $authRow = $this->getAuthRow(self::PROVIDER_EQUALS, $username);
74
75 4
        if (!$authRow) {
76
            throw new AuthException("User not found");
77
        }
78
79
        // verify password
80 4
        if (!$this->callVerifyFunction($password, $authRow->token)) {
81 2
            throw new AuthException("Wrong password");
82
        }
83
84
        // get auth row
85 2
        return $authRow;
86
    }
87
88
    /**
89
     * Authenticate user by login/pass
90
     *
91
     * @param Users\Row $user
92
     * @param string    $password
93
     *
94
     * @throws AuthException
95
     * @return Row
96
     */
97
    public function generateEquals($user, $password)
98
    {
99
        // clear previous generated Auth record
100
        // works with change password
101
        self::delete(
102
            [
103
                'userId' => $user->id,
104
                'foreignKey' => $user->login,
105
                'provider' => self::PROVIDER_EQUALS,
106
                'tokenType' => self::TYPE_ACCESS
107
            ]
108
        );
109
110
        // new auth row
111
        $row = new Row();
112
        $row->userId = $user->id;
113
        $row->foreignKey = $user->login;
114
        $row->provider = self::PROVIDER_EQUALS;
115
        $row->tokenType = self::TYPE_ACCESS;
116
117
        // generate secret part is not required
118
        // encrypt password and save as token
119
        $row->token = $this->callHashFunction($password);
120
121
        $row->save();
122
123
        return $row;
124
    }
125
126
    /**
127
     * Generates cookie for authentication
128
     *
129
     * @throws \Bluz\Db\Exception\DbException
130
     */
131
    public function generateCookie()
132
    {
133
        $hash = hash('md5', microtime(true));
134
        $ttl = Config::getModuleData('users', 'rememberMe');
135
136
        self::delete(
137
            [
138
                'userId' => Auth::getIdentity()->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
139
                'foreignKey' => Auth::getIdentity()->login,
0 ignored issues
show
Bug introduced by
Accessing login on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
140
                'provider' => self::PROVIDER_COOKIE,
141
                'tokenType' => self::TYPE_ACCESS,
142
            ]
143
        );
144
145
        $row = new Row();
146
        $row->userId = Auth::getIdentity()->id;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
147
        $row->foreignKey = Auth::getIdentity()->login;
0 ignored issues
show
Bug introduced by
Accessing login on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
148
        $row->provider = self::PROVIDER_COOKIE;
149
        $row->tokenType = self::TYPE_ACCESS;
150
        $row->expired = gmdate('Y-m-d H:i:s', time() + $ttl);
151
152
        $row->tokenSecret = $this->generateSecret(Auth::getIdentity()->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
153
        $row->token = hash('md5', $row->tokenSecret . $hash);
154
155
        $row->save();
156
157
        Response::setCookie('rToken', $hash, time() + $ttl, '/');
158
        Response::setCookie('rId', Auth::getIdentity()->id, time() + $ttl, '/');
0 ignored issues
show
Bug introduced by
Accessing id on the interface Bluz\Auth\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
159
    }
160
161
    /**
162
     * Authenticate via cookie
163
     *
164
     * @param $userId
165
     * @param $token
166
     *
167
     * @throws AuthException
168
     * @throws Exception
169
     */
170
    public function authenticateCookie($userId, $token)
171
    {
172
        $authRow = $this->checkCookie($userId, $token);
173
174
        // get user row
175
        $user = Users\Table::findRow($authRow->userId);
176
177
        // try to login
178
        $user->tryLogin();
179
    }
180
181
    /**
182
     * Check if supplied cookie is valid
183
     *
184
     * @param $userId
185
     * @param $token
186
     *
187
     * @return Row
188
     * @throws AuthException
189
     */
190
    public function checkCookie($userId, $token)
191
    {
192
        if (!$authRow = $this->findRowWhere(['userId' => $userId, 'provider' => self::PROVIDER_COOKIE])) {
193
            throw new AuthException('User not found');
194
        }
195
196
        if (strtotime($authRow->expired) < time()) {
197
            $this->removeCookieToken($userId);
198
            throw new AuthException('Token has expired');
199
        }
200
201
        if ($authRow->token != hash('md5', $authRow->tokenSecret . $token)) {
202
            throw new AuthException('Incorrect token');
203
        }
204
205
        return $authRow;
206
    }
207
208
    /**
209
     * Removes a cookie-token from database
210
     *
211
     * @param $userId
212
     *
213
     * @throws \Bluz\Db\Exception\DbException
214
     */
215 1
    public function removeCookieToken($userId)
216
    {
217 1
        self::delete(
218
            [
219 1
                'userId' => $userId,
220 1
                'provider' => self::PROVIDER_COOKIE
221
            ]
222
        );
223 1
    }
224
225
    /**
226
     * Call Hash Function
227
     *
228
     * @param string $password
229
     *
230
     * @throws \Application\Exception
231
     * @return string
232
     */
233 1
    protected function callHashFunction($password)
234
    {
235
        /** @var \Bluz\Auth\Auth $auth */
236 1
        $auth = Auth::getInstance();
237 1
        $options = $auth->getOption(self::PROVIDER_EQUALS);
238
239 1
        if (!isset($options['hash']) or
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
240 1
            !is_callable($options['hash'])
241
        ) {
242
            throw new Exception("Hash function for 'equals' adapter is not callable");
243
        }
244
245
        // encrypt password with secret
246 1
        return call_user_func($options['hash'], $password);
247
    }
248
249
    /**
250
     * Call Verify Function
251
     *
252
     * @param string $password
253
     * @param string $hash
254
     *
255
     * @throws \Application\Exception
256
     * @return string
257
     */
258 4
    protected function callVerifyFunction($password, $hash)
259
    {
260
        /** @var \Bluz\Auth\Auth $auth */
261 4
        $auth = Auth::getInstance();
262 4
        $options = $auth->getOption(self::PROVIDER_EQUALS);
263
264 4
        if (!isset($options['verify']) or
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
265 4
            !is_callable($options['verify'])
266
        ) {
267
            throw new Exception("Verify function for 'equals' adapter is not callable");
268
        }
269
270
        // verify password with hash
271 4
        return call_user_func($options['verify'], $password, $hash);
272
    }
273
274
    /**
275
     * authenticate user by token
276
     *
277
     * @param string $token
278
     *
279
     * @throws \Bluz\Auth\AuthException
280
     * @return void
281
     */
282
    public function authenticateToken($token)
283
    {
284
        $authRow = $this->checkToken($token);
285
286
        // get user profile
287
        $user = Users\Table::findRow($authRow->userId);
288
289
        // try to login
290
        $user->tryLogin();
291
    }
292
293
    /**
294
     * authenticate user by token
295
     *
296
     * @param string $token
297
     *
298
     * @throws \Bluz\Auth\AuthException
299
     * @return Row
300
     */
301
    public function checkToken($token)
302
    {
303
        if (!$authRow = $this->findRowWhere(['token' => $token, 'provider' => self::PROVIDER_TOKEN])) {
304
            throw new AuthException('Invalid token');
305
        }
306
307
        if ($authRow->expired < gmdate('Y-m-d H:i:s')) {
308
            throw new AuthException('Token has expired');
309
        }
310
311
        return $authRow;
312
    }
313
314
    /**
315
     * @param $equalAuth
316
     *
317
     * @return Row
318
     * @throws Exception
319
     * @throws \Bluz\Db\Exception\DbException
320
     */
321 1
    public function generateToken($equalAuth)
322
    {
323
        // clear previous generated Auth record
324
        // works with change password
325 1
        self::delete(
326
            [
327 1
                'userId' => $equalAuth->userId,
328 1
                'foreignKey' => $equalAuth->foreignKey,
329 1
                'provider' => self::PROVIDER_TOKEN,
330 1
                'tokenType' => self::TYPE_ACCESS,
331
            ]
332
        );
333
        // new auth row
334 1
        $row = new Row();
335 1
        $row->userId = $equalAuth->userId;
336 1
        $row->foreignKey = $equalAuth->foreignKey;
337 1
        $row->provider = self::PROVIDER_TOKEN;
338 1
        $row->tokenType = self::TYPE_ACCESS;
339 1
        $row->expired = gmdate('Y-m-d H:i:s', time() + self::TOKEN_EXPIRATION_TIME);
340
341
        // generate secret
342 1
        $row->tokenSecret = $this->generateSecret($equalAuth->userId);
343
344
        // encrypt password and save as token
345 1
        $row->token = $this->callHashFunction($equalAuth->token);
346
347 1
        $row->save();
348
349 1
        return $row;
350
    }
351
}
352