Passed
Push — develop ( 87ef83...1cf4ee )
by nguereza
02:55
created

SessionAuthentication::getUserData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 SessionAuthentication.php
34
 *
35
 *  The Authentication using session feature 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   https://www.platine-php.com
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\Framework\App\Application;
51
use Platine\Framework\Auth\AuthenticationInterface;
52
use Platine\Framework\Auth\Entity\User;
53
use Platine\Framework\Auth\Event\AuthInvalidPasswordEvent;
54
use Platine\Framework\Auth\Event\AuthLoginEvent;
55
use Platine\Framework\Auth\Exception\AccountLockedException;
56
use Platine\Framework\Auth\Exception\AccountNotFoundException;
57
use Platine\Framework\Auth\Exception\InvalidCredentialsException;
58
use Platine\Framework\Auth\Exception\MissingCredentialsException;
59
use Platine\Framework\Auth\IdentityInterface;
60
use Platine\Framework\Auth\Repository\UserRepository;
61
use Platine\Security\Hash\HashInterface;
62
use Platine\Session\Session;
63
64
/**
65
 * class SessionAuthentication
66
 * @package Platine\Framework\Auth\Authentication
67
 */
68
class SessionAuthentication implements AuthenticationInterface
69
{
70
    /**
71
     * The session instance to use
72
     * @var Session
73
     */
74
    protected Session $session;
75
76
    /**
77
     * The user repository instance
78
     * @var UserRepository
79
     */
80
    protected UserRepository $userRepository;
81
82
    /**
83
     * Hash instance to use
84
     * @var HashInterface
85
     */
86
    protected HashInterface $hash;
87
88
    /**
89
     * The application instance
90
     * @var Application
91
     */
92
    protected Application $app;
93
94
    /**
95
     * Create new instance
96
     * @param Application $app
97
     * @param HashInterface $hash
98
     * @param Session $session
99
     * @param UserRepository $userRepository
100
     */
101
    public function __construct(
102
        Application $app,
103
        HashInterface $hash,
104
        Session $session,
105
        UserRepository $userRepository
106
    ) {
107
        $this->app = $app;
108
        $this->hash = $hash;
109
        $this->session = $session;
110
        $this->userRepository = $userRepository;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function getUser(): IdentityInterface
117
    {
118
        if (!$this->isLogged()) {
119
            throw new AccountNotFoundException('User not logged', 401);
120
        }
121
122
        $id = $this->session->get('user.id');
123
        $user = $this->userRepository->find($id);
124
125
        if (!$user) {
126
            throw new AccountNotFoundException(
127
                'Can not find the logged user information, may be data is corrupted',
128
                401
129
            );
130
        }
131
132
        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...
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function isLogged(): bool
139
    {
140
        return $this->session->has('user');
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function login(array $credentials = [], bool $remeberMe = false): bool
147
    {
148
        if (!isset($credentials['username']) || !isset($credentials['password'])) {
149
            throw new MissingCredentialsException(
150
                'Missing username or password information',
151
                401
152
            );
153
        }
154
155
        $username = $credentials['username'];
156
        $password = $credentials['password'];
157
158
        $user = $this->getUserEntity($username, $password);
159
        if ($user === null) {
160
            throw new AccountNotFoundException('Can not find the user with the given information', 401);
161
        } elseif ($user->status === 'D') {
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
162
            throw new AccountLockedException(
163
                'User is locked',
164
                401
165
            );
166
        }
167
168
        if (!$this->hash->verify($password, $user->password)) {
0 ignored issues
show
Bug Best Practice introduced by
The property password does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
169
            $this->app->dispatch(new AuthInvalidPasswordEvent($user));
170
171
            throw new InvalidCredentialsException(
172
                'Invalid credentials',
173
                401
174
            );
175
        }
176
177
        $permissions = [];
178
179
        $roles = $user->roles;
0 ignored issues
show
Bug Best Practice introduced by
The property roles does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
180
        foreach ($roles as $role) {
181
            $rolePermissions = $role->permissions;
182
            foreach ($rolePermissions as $permission) {
183
                $permissions[] = $permission->code;
184
            }
185
        }
186
187
        $data = [
188
          'id' => $user->id,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
189
          'username' => $user->username,
0 ignored issues
show
Bug Best Practice introduced by
The property username does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
190
          'lastname' => $user->lastname,
0 ignored issues
show
Bug Best Practice introduced by
The property lastname does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
191
          'firstname' => $user->firstname,
0 ignored issues
show
Bug Best Practice introduced by
The property firstname does not exist on Platine\Framework\Auth\Entity\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
192
          'permissions' => array_unique($permissions),
193
        ];
194
195
        $this->session->set('user', array_merge($data, $this->getUserData($user)));
196
197
        $this->app->dispatch(new AuthLoginEvent($user));
198
199
        return $this->isLogged();
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function logout(): void
206
    {
207
        $this->session->remove('user');
208
    }
209
210
    /**
211
     * Return the user entity
212
     * @param string $username
213
     * @param string $password
214
     * @return User|null
215
     */
216
    protected function getUserEntity(string $username, string $password): ?User
0 ignored issues
show
Unused Code introduced by
The parameter $password is not used and could be removed. ( Ignorable by Annotation )

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

216
    protected function getUserEntity(string $username, /** @scrutinizer ignore-unused */ string $password): ?User

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
217
    {
218
        return $this->userRepository
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->userReposi...sername' => $username)) could return the type Platine\Orm\Entity which includes types incompatible with the type-hinted return Platine\Framework\Auth\Entity\User|null. Consider adding an additional type-check to rule them out.
Loading history...
219
                                    ->with('roles.permissions')
220
                                    ->findBy(['username' => $username]);
221
    }
222
223
    /**
224
     * Return the user additional data
225
     * @param User $user
226
     * @return array<string, mixed>
227
     */
228
    protected function getUserData(User $user): array
0 ignored issues
show
Unused Code introduced by
The parameter $user is not used and could be removed. ( Ignorable by Annotation )

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

228
    protected function getUserData(/** @scrutinizer ignore-unused */ User $user): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
229
    {
230
        return [];
231
    }
232
}
233