Passed
Push — develop ( 36c2b4...1d782e )
by nguereza
03:02
created

JWTAuthentication::getUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 17
rs 10
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file JWTAuthentication.php
34
 *
35
 *  The Authentication using JWT class
36
 *
37
 *  @package    Platine\Framework\Auth\Authentication
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   http://www.iacademy.cf
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\Auth\Authentication;
49
50
use Platine\Config\Config;
51
use Platine\Framework\Auth\ApiAuthenticationInterface;
52
use Platine\Framework\Auth\Exception\AccountLockedException;
53
use Platine\Framework\Auth\Exception\AccountNotFoundException;
54
use Platine\Framework\Auth\Exception\InvalidCredentialsException;
55
use Platine\Framework\Auth\Exception\MissingCredentialsException;
56
use Platine\Framework\Auth\IdentityInterface;
57
use Platine\Framework\Auth\Repository\UserRepository;
58
use Platine\Framework\Security\JWT\Exception\JWTException;
59
use Platine\Framework\Security\JWT\JWT;
60
use Platine\Http\ServerRequestInterface;
61
use Platine\Logger\LoggerInterface;
62
use Platine\Security\Hash\HashInterface;
63
use Platine\Stdlib\Helper\Str;
64
65
/**
66
 * @class JWTAuthentication
67
 * @package Platine\Framework\Auth\Authentication
68
 * @template T
69
 */
70
class JWTAuthentication implements ApiAuthenticationInterface
71
{
72
73
    /**
74
     * The JWT instance
75
     * @var JWT
76
     */
77
    protected JWT $jwt;
78
79
    /**
80
     * The logger instance
81
     * @var LoggerInterface
82
     */
83
    protected LoggerInterface $logger;
84
85
    /**
86
     * The configuration instance
87
     * @var Config<T>
88
     */
89
    protected Config $config;
90
91
    /**
92
     * The user repository instance
93
     * @var UserRepository
94
     */
95
    protected UserRepository $userRepository;
96
97
    /**
98
     * Hash instance to use
99
     * @var HashInterface
100
     */
101
    protected HashInterface $hash;
102
103
    /**
104
     * Create new instance
105
     * @param JWT $jwt
106
     * @param LoggerInterface $logger
107
     * @param Config<T> $config
108
     * @param HashInterface $hash
109
     * @param UserRepository $userRepository
110
     */
111
    public function __construct(
112
        JWT $jwt,
113
        LoggerInterface $logger,
114
        Config $config,
115
        HashInterface $hash,
116
        UserRepository $userRepository
117
    ) {
118
        $this->jwt = $jwt;
119
        $this->logger = $logger;
120
        $this->config = $config;
121
        $this->hash = $hash;
122
        $this->userRepository = $userRepository;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function getUser(): IdentityInterface
129
    {
130
        //if (!$this->isLogged()) {
131
           // throw new AccountNotFoundException('User not logged', 401);
132
        //}
133
134
        // $id = $this->session->get('user.id');
135
        $user = $this->userRepository->find(1);
136
137
        if (!$user) {
138
            throw new AccountNotFoundException(
139
                'Can not find the logged user information, may be data is corrupted',
140
                401
141
            );
142
        }
143
144
        return $user;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $user returns the type Platine\Orm\Entity which is incompatible with the type-hinted return Platine\Framework\Auth\IdentityInterface.
Loading history...
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function isAuthenticated(ServerRequestInterface $request): bool
151
    {
152
        $headerName = $this->config->get('api.auth.headers.name', 'Authorization');
153
        $tokenHeader = $request->getHeaderLine($headerName);
154
        if (empty($tokenHeader)) {
155
            $this->logger->error('API authentication failed missing token header');
156
157
            return false;
158
        }
159
        $tokenType = $this->config->get('api.auth.headers.token_type', 'Bearer');
160
        $secret = $this->config->get('api.sign.secret', '');
161
162
        $token = Str::replaceFirst($tokenType . ' ', '', $tokenHeader);
163
164
        $this->jwt->setSecret($secret);
165
        try {
166
            $this->jwt->decode($token);
167
168
            return true;
169
        } catch (JWTException $ex) {
170
            $this->logger->error('API authentication failed: {message}', [
171
                'message' => $ex->getMessage(),
172
            ]);
173
        }
174
175
        return false;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function login(array $credentials = []): array
182
    {
183
        if (!isset($credentials['username']) || !isset($credentials['password'])) {
184
            throw new MissingCredentialsException(
185
                'Missing username or password information',
186
                401
187
            );
188
        }
189
190
        $username = $credentials['username'];
191
        $password = $credentials['password'];
192
        $user = $this->userRepository
193
                    ->with('roles.permissions')
194
                    ->findBy(['username' => $username]);
195
196
        if (!$user) {
197
            throw new AccountNotFoundException('Can not find the user with the given information', 401);
198
        } elseif ($user->status === 'D') {
199
            throw new AccountLockedException(
200
                'User is locked',
201
                401
202
            );
203
        }
204
205
        if (!$this->hash->verify($password, $user->password)) {
206
            throw new InvalidCredentialsException(
207
                'Invalid credentials',
208
                401
209
            );
210
        }
211
212
        $permissions = [];
213
214
        $roles = $user->roles;
215
        foreach ($roles as $role) {
216
            $rolePermissions = $role->permissions;
217
            foreach ($rolePermissions as $permission) {
218
                $permissions[] = $permission->code;
219
            }
220
        }
221
222
        $secret = $this->config->get('api.sign.secret');
223
        $expire = $this->config->get('api.auth.expire', 1800);
224
        $tokenExpire = time() + $expire;
225
        $this->jwt->setSecret($secret)
0 ignored issues
show
Bug introduced by
It seems like $secret can also be of type null; however, parameter $secret of Platine\Framework\Security\JWT\JWT::setSecret() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

225
        $this->jwt->setSecret(/** @scrutinizer ignore-type */ $secret)
Loading history...
226
                  ->setPayload([
227
                      'id' => $user->id,
228
                      'exp' => $tokenExpire,
229
                  ])
230
                  ->sign();
231
232
        $data = [
233
          'user' => [
234
            'id' => $user->id,
235
            'username' => $user->username,
236
            'lastname' => $user->lastname,
237
            'firstname' => $user->firstname,
238
            'permissions' => array_unique($permissions),
239
          ],
240
          'token' => $this->jwt->getToken(),
241
          'refresh_token' => '',
242
          'expire_at' => $tokenExpire,
243
        ];
244
245
        return $data;
246
    }
247
248
    /**
249
     * {@inheritdoc}
250
     */
251
    public function logout(): void
252
    {
253
    }
254
}
255