1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace App\Middleware; |
4
|
|
|
|
5
|
|
|
use App\Common\Acl; |
6
|
|
|
use App\Common\Auth; |
7
|
|
|
use App\Common\JsonException; |
8
|
|
|
use App\Model\AccessToken; |
9
|
|
|
|
10
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
11
|
|
|
use Psr\Http\Message\ResponseInterface; |
12
|
|
|
|
13
|
|
|
class Authentication |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var Acl |
17
|
|
|
*/ |
18
|
|
|
private $acl; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var |
22
|
|
|
*/ |
23
|
|
|
private $settings; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Authentication constructor. |
27
|
|
|
* |
28
|
|
|
* @param Acl $acl |
29
|
|
|
* @param $settings |
30
|
|
|
*/ |
31
|
|
|
public function __construct(Acl $acl, $settings) |
32
|
|
|
{ |
33
|
|
|
$this->acl = $acl; |
34
|
|
|
$this->settings = $settings; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Execute the middleware. |
39
|
|
|
* |
40
|
|
|
* @param ServerRequestInterface $request |
41
|
|
|
* @param ResponseInterface $response |
42
|
|
|
* @param callable $next |
43
|
|
|
* |
44
|
|
|
* @return ResponseInterface |
45
|
|
|
* @throws JsonException |
46
|
|
|
*/ |
47
|
|
|
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) |
48
|
|
|
{ |
49
|
|
|
// Request has to have Authorization header |
50
|
|
|
if (!$request->getHeader('Authorization')) { |
51
|
|
|
return $this->failNotAuthorized(); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
// HTTP Authorization header available |
55
|
|
|
// Authorization: Bearer XXXXXXXXXXXXXXXXXXX |
56
|
|
|
// fetch XXXXXXXXXXXXX part |
57
|
|
|
$token = @explode(' ', @$request->getHeader('Authorization')[0]); |
58
|
|
|
$token = is_array($token) && (count($token) == 2) ? $token[1] : null; |
59
|
|
|
if (empty($token)) { |
60
|
|
|
return $this->failNotAuthorized(); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
// provided token must be valid |
64
|
|
|
if (!AccessToken::validateToken($token, $this->settings['params']['allowHosts'])) { |
65
|
|
|
return $this->failNotAuthorized(); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
// find user by token |
69
|
|
|
$user = AccessToken::getUserByToken($token); |
70
|
|
|
if (empty($user)) { |
71
|
|
|
return $this->failNotAuthorized(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
Auth::setUser($user); |
75
|
|
|
|
76
|
|
|
$isAllowed = false; |
77
|
|
|
$route = $request->getAttribute('route'); |
78
|
|
|
|
79
|
|
|
if ($route) { |
80
|
|
|
// check access for the route |
81
|
|
|
$resource = Acl::buildResourceName(Acl::GUARD_TYPE_ROUTE, $route->getPattern()); |
82
|
|
|
$privilege = Acl::getPrivilegeByHTTPMethod($request->getMethod()); |
83
|
|
View Code Duplication |
if ($this->acl->hasResource($resource)) { |
|
|
|
|
84
|
|
|
$isAllowed = $isAllowed || $this->acl->isAllowed($user->role->name, $resource, $privilege); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
// check access for the callable |
88
|
|
|
$resource = Acl::buildResourceName(Acl::GUARD_TYPE_CALLABLE, $route->getCallable()); |
89
|
|
|
$privilege = null; |
90
|
|
View Code Duplication |
if ($this->acl->hasResource($resource)) { |
|
|
|
|
91
|
|
|
$isAllowed = $isAllowed || $this->acl->isAllowed($user->role->name, $resource, $privilege); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
if (!$isAllowed) { |
95
|
|
|
// access is not allowed |
96
|
|
|
return $this->failNotAllowed(); |
97
|
|
|
} |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
// access allowed, move to next middleware |
101
|
|
|
return $next($request, $response); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Produce HTTP 401 Not authorized |
106
|
|
|
* |
107
|
|
|
* @throws JsonException |
108
|
|
|
*/ |
109
|
|
|
protected function failNotAuthorized() |
110
|
|
|
{ |
111
|
|
|
throw new JsonException(null, 401, 'Not authorized', 'The user must be authorized'); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Produce HTTP 403 Not allowed |
116
|
|
|
* |
117
|
|
|
* @throws JsonException |
118
|
|
|
*/ |
119
|
|
|
protected function failNotAllowed() |
120
|
|
|
{ |
121
|
|
|
throw new JsonException(null, 403, 'Not allowed', ' Access to this location is not allowed'); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.