Completed
Branch master (bb320a)
by Pierre
03:04 queued 48s
created

Jwt::isValidAuthorization()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
3
namespace App\Middlewares;
4
5
use App\Http\Request;
6
use App\Tools\Jwt\Token;
7
use App\Http\Interfaces\Middleware\ILayer;
8
use App\Container;
9
10
/**
11
 * App\Middleware\Jwt
12
 *
13
 * Intercept jwt header and auth if required
14
 */
15
class Jwt implements ILayer
16
{
17
18
    use \App\Middlewares\Reuse\TInit;
19
20
    const _SIGN = 'X-Middleware-Jwt';
21
    const _PASSWORD = 'password';
22
    const _EMAIL = 'email';
23
    const _USER_STATUS = 'status';
24
    const _VALID = 'valid';
25
    const _AUTORIZATION = 'Authorization';
26
    const _ERROR = 'error';
27
    const _ERROR_MESSAGE = 'errorMessage';
28
29
    /**
30
     * peel
31
     *
32
     * @param array $container
33
     * @param \Closure $next
34
     * @return \Closure
35
     */
36 14
    public function peel(Container $container, \Closure $next)
37
    {
38 14
        $this->init($container);
39 14
        $this->process();
40 14
        return $next($container);
41
    }
42
43
    /**
44
     * process
45
     *
46
     */
47 4
    protected function process()
48
    {
49 4
        if ($this->enabled) {
50 4
            $this->response->getHeaderManager()->add(
51 4
                self::_SIGN,
52 4
                microtime(true)
53
            );
54 4
            if ($this->required()) {
55 4
                if ($this->isValidAuthorization()) {
56 4
                    $toolJwtToken = new Token($this->config, $this->request);
57
                    try {
58 4
                        $tFrags = explode(' ', $this->headers[self::_AUTORIZATION]);
59 4
                        $decodedToken = $toolJwtToken->decode(trim($tFrags[1]));
60 4
                        if (isset($decodedToken->{Token::_DATA}->{Token::_DATA_ID})) {
61 4
                            $userId = $decodedToken->{Token::_DATA}->{Token::_DATA_ID};
62 4
                            $user = $this->getUser($userId);
63 4
                            if ($user !== false) {
0 ignored issues
show
introduced by
The condition $user !== false is always true.
Loading history...
64 4
                                if ($this->isValidCredential($decodedToken, $user)) {
65 1
                                    $this->request->setSession('auth', $user, 'user');
66
                                } else {
67 2
                                    $this->sendError(403, 'bad credentials');
68
                                }
69
                            }
70
                        }
71 2
                    } catch (\Exception $e) {
72 2
                        $this->sendError(500, $e->getMessage());
73
                    }
74
                }
75
            }
76
        }
77
    }
78
79
    /**
80
     * send response and die
81
     *
82
     * @param integer $errorCode
83
     * @return void
84
     */
85 1
    protected function sendError(int $errorCode, string $errMsg)
86
    {
87
        $errorMsg = [
88 1
            self::_ERROR => true,
89 1
            self::_ERROR_MESSAGE => 'Auth failed : ' . $errMsg
90
        ];
91 1
        $this->response
92 1
            ->setCode($errorCode)
93 1
            ->setContent($errorMsg)
94 1
            ->send();
95 1
        if (false === $this->request->isCli()) {
96
            die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
97
        }
98
    }
99
100
    /**
101
     * isPreflight
102
     *
103
     * @return bool
104
     */
105 1
    protected function isPreflight(): bool
106
    {
107 1
        $isOptionsMethod = ($this->request->getMethod() == Request::METHOD_OPTIONS);
108 1
        $corsHeadersKeys = array_keys($this->headers);
109 1
        $hasOrigin = in_array('Origin', $corsHeadersKeys);
110 1
        $hasACRequestMethod = in_array(
111 1
            'Access-Control-Request-Method',
112 1
            $corsHeadersKeys
113
        );
114 1
        return ($isOptionsMethod && $hasOrigin && $hasACRequestMethod);
115
    }
116
117
    /**
118
     * isValidCredential
119
     *
120
     * @param object $decodedToken
121
     * @param array $user
122
     * @return boolean
123
     */
124 1
    protected function isValidCredential($decodedToken, $user): bool
125
    {
126 1
        $login = $decodedToken->{Token::_DATA}->{Token::_DATA_LOGIN};
127 1
        $passwordHash = $decodedToken->{Token::_DATA}->{Token::_DATA_PASSWORD_HASH};
128 1
        $checkLogin = ($login === $user[self::_EMAIL]);
129 1
        $checkPassword = password_verify($user[self::_PASSWORD], $passwordHash);
130 1
        $checkStatus = ($user[self::_USER_STATUS] === self::_VALID);
131 1
        return ($checkLogin && $checkPassword && $checkStatus);
132
    }
133
134
    /**
135
     * getUser
136
     *
137
     * @param int $userId
138
     * @return array
139
     */
140 1
    protected function getUser(int $userId): array
141
    {
142 1
        $authModel = new \App\Model\Users($this->config);
143 1
        $userList = $authModel->getById($userId);
144 1
        return isset($userList[0]) ? $userList[0] : $userList;
145
    }
146
147
    /**
148
     * isValidAuthorization
149
     *
150
     * @return boolean
151
     */
152 1
    protected function isValidAuthorization(): bool
153
    {
154 1
        return (isset($this->headers[self::_AUTORIZATION])
155 1
            && !empty($this->headers[self::_AUTORIZATION]));
156
    }
157
158
    /**
159
     * required
160
     *
161
     * @return boolean
162
     */
163 1
    protected function required(): bool
164
    {
165 1
        return (!$this->isExclude()
166 1
            && $this->requestUriPrefix() === $this->prefix
167 1
            && !$this->isPreflight());
168
    }
169
170
    /**
171
     * isExclude
172
     *
173
     * @return boolean
174
     */
175 1
    protected function isExclude(): bool
176
    {
177 1
        $disallowed = $this->configParams[self::_EXCLUDE];
178 1
        $count = count($disallowed);
179 1
        for ($c = 0; $c < $count; ++$c) {
180 1
            $composed = $this->prefix . $disallowed[$c];
181 1
            $isExclude = ($composed == $this->request->getUri());
182 1
            if ($isExclude) {
183 1
                return true;
184
            }
185
        }
186 1
        return false;
187
    }
188
189
    /**
190
     * uriPrefix
191
     *
192
     * @return string
193
     */
194 1
    protected function requestUriPrefix(): string
195
    {
196 1
        return substr($this->request->getUri(), 0, strlen($this->prefix));
197
    }
198
}
199