Test Failed
Push — master ( 8c09b3...bc5138 )
by Pierre
03:23
created

Auth::lostpassword()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 40
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 34
nc 7
nop 0
dl 0
loc 40
rs 9.0648
c 1
b 0
f 0
ccs 0
cts 0
cp 0
crap 30
1
<?php
2
3
namespace App\Controllers\Api\V1;
4
5
use App\Interfaces\Controllers\IApi;
6
use App\Reuse\Controllers\AbstractApi;
7
use Nymfonya\Component\Config;
8
use Nymfonya\Component\Container;
9
use Nymfonya\Component\Http\Headers;
10
use Nymfonya\Component\Http\Request;
11
use Nymfonya\Component\Http\Response;
12
use App\Component\Db\Core;
13
use App\Model\Repository\Users;
14
use App\Component\Jwt\Token;
15
use App\Component\Auth\Factory;
16
use App\Component\Crypt;
17
use Egulias\EmailValidator\EmailValidator;
18
use Egulias\EmailValidator\Validation\RFCValidation;
19
/*use Swift_SmtpTransport;
20
use Swift_Mailer;
21
use Swift_Message;*/
22
use App\Component\Mailer\Smtp;
23 7
use Exception;
24
25 7
final class Auth extends AbstractApi implements IApi
26
{
27
28
    /**
29
     * core db instance
30
     *
31
     * @var Core
32
     */
33
    protected $db;
34 3
35
    /**
36 3
     * user repository
37 3
     *
38 3
     * @var Users
39 3
     */
40 3
    protected $userRepository;
41 1
42 1
    /**
43 1
     * slugs
44 1
     *
45
     * @var array
46
     */
47 2
    protected $slugs;
48 2
49 1
    /**
50
     * sql
51 1
     *
52 1
     * @var String
53 1
     */
54 1
    protected $sql;
55 1
56 1
    /**
57 1
     * sql values to bind statement
58
     *
59 1
     * @var array
60 1
     */
61 1
    protected $bindValues;
62 1
63 1
    /**
64
     * error
65 1
     *
66
     * @var Boolean
67 1
     */
68 1
    protected $error;
69 1
70 1
    /**
71
     * error message
72
     *
73
     * @var String
74
     */
75
    protected $errorMessage;
76
77
    /**
78
     * instanciate
79 1
     *
80
     * @param Container $container
81 1
     */
82 1
    public function __construct(Container $container)
83 1
    {
84
        $this->userRepository = new Users($container);
85
        $this->db = new Core($container);
86
        $this->db->fromOrm($this->userRepository);
87
        $this->error = false;
88
        $this->errorMessage = '';
89
        parent::__construct($container);
90
    }
91
92
    /**
93
     * login action
94 1
     *
95
     * @Role anonymous
96 1
     * @return Auth
97 1
     */
98 1
    final public function login(): Auth
99
    {
100
        $config = $this->getService(Config::class);
101
        $logger = $this->getService(\Monolog\Logger::class);
102
        $login = filter_var($this->request->getParam('login'), FILTER_SANITIZE_EMAIL);
103
        $password = filter_var($this->request->getParam('password'), FILTER_SANITIZE_STRING);
104
        if (false === $this->isValidLogin($login, $password)) {
105
            $logger->warning(__FUNCTION__ . ' Invalid arguments');
106
            return $this->setErrorResponse(
107
                Response::HTTP_BAD_REQUEST,
108 1
                'Invalid arguments'
109
            );
110 1
        }
111 1
        $authFactory = new Factory($this->getContainer());
112 1
        $authFactory->setAdapter();
113 1
        if ($user = $authFactory->auth($login, $password)) {
114 1
            $jwtToken = new Token($config, $this->request);
115 1
            $token = $jwtToken
116 1
                ->setIssueAt(time())
117 1
                ->setIssueAtDelay(0)
118 1
                ->setTtl(1200)
119
                ->encode(
120 1
                    $user[Factory::_ID],
121
                    $user[Factory::_EMAIL],
122
                    $user[Factory::_PASSWORD]
123
                );
124
            $logger->info(__FUNCTION__ . ' Auth succeed');
125
            $this->response
126
                ->setCode(Response::HTTP_OK)
127
                ->setContent(
128
                    [Response::_ERROR => false, 'token' => $token]
129
                );
130
            return $this;
131
        }
132
        $logger->warning(__FUNCTION__ . ' Auth failed');
133
        unset($authFactory);
134
        return $this->setErrorResponse(
135
            Response::HTTP_UNAUTHORIZED,
136
            'Bad credentials'
137
        );
138
    }
139
140
    /**
141
     * register action
142
     *
143
     * @Role anonymous
144
     * @return Auth
145
     */
146
    final public function register(): Auth
147
    {
148
        $config = $this->getService(Config::class);
149
        $logger = $this->getService(\Monolog\Logger::class);
150
        $name = filter_var($this->request->getParam('name'), FILTER_SANITIZE_STRING);
151
        $email = filter_var($this->request->getParam('email'), FILTER_SANITIZE_EMAIL);
152
        $password = filter_var($this->request->getParam('password'), FILTER_SANITIZE_STRING);
153
        if (false === $this->isValidRegistration($name, $email, $password)) {
154
            $this->error = true;
155
            $this->errorMessage = 'Invalid arguments';
156
            $logger->warning(__FUNCTION__ . ' Invalid arguments');
157
            return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
158
        }
159
        $this->userRepository->emailExists($email);
0 ignored issues
show
Bug introduced by
The method emailExists() does not exist on App\Model\Repository\Users. ( Ignorable by Annotation )

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

159
        $this->userRepository->/** @scrutinizer ignore-call */ 
160
                               emailExists($email);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
160
        $this->sql = $this->userRepository->getSql();
161
        $this->bindValues = $this->userRepository->getBuilderValues();
162
        $this->db->run($this->sql, $this->bindValues)->hydrate();
163
        $emailCount = $this->db->getRowset();
164
        $emailCounter = (int) $emailCount[0]['counter'];
165
        if ($emailCounter !== 0) {
166
            $this->error = true;
167
            $this->errorMessage = 'Email exists';
168
            $logger->warning(__FUNCTION__ . $this->errorMessage);
169
            return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
170
        }
171
        $cryptEngine = new Crypt($config);
172
        $cryptedPassword = $cryptEngine->encrypt($password);
173
        unset($cryptEngine);
174
        $this->userRepository->register($name, $email, $cryptedPassword);
0 ignored issues
show
Bug introduced by
The method register() does not exist on App\Model\Repository\Users. ( Ignorable by Annotation )

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

174
        $this->userRepository->/** @scrutinizer ignore-call */ 
175
                               register($name, $email, $cryptedPassword);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
175
        $this->sql = $this->userRepository->getSql();
176
        $this->bindValues = $this->userRepository->getBuilderValues();
177
        $this->db->run($this->sql, $this->bindValues);
178
        return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
179
    }
180
181
    /**
182
     * lost password action
183
     *
184
     * @Role anonymous
185
     * @return Auth
186
     */
187
    final public function lostpassword(): Auth
188
    {
189
        $logger = $this->getService(\Monolog\Logger::class);
190
        $email = filter_var($this->request->getParam('email'), FILTER_SANITIZE_EMAIL);
191
        $validator = new EmailValidator();
192
        $isValid = $validator->isValid($email, new RFCValidation());
193
        if (false === $isValid) {
194
            $this->error = true;
195
            $this->errorMessage = 'Invalid email';
196
            $logger->warning(__FUNCTION__ . ' Invalid email');
197
            return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
198
        }
199
        $this->userRepository->getByEmail($email);
200
        $this->sql = $this->userRepository->getSql();
201
        $this->bindValues = $this->userRepository->getBuilderValues();
202
        $this->db->run($this->sql, $this->bindValues)->hydrate();
203
        $users = $this->db->getRowset();
204
        $emailExists = isset($users[0]);
205
        if (false === $emailExists) {
206
            $this->error = true;
207
            $this->errorMessage = 'Email does not exists';
208
            $logger->warning(__FUNCTION__ . $this->errorMessage);
209
            return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
210
        }
211
        $user = $users[0];
212
        try {
213
            $mailer = new Smtp($this->getContainer());
214
            $mailer
215
                ->setTo([$email => $user['name']])
216
                ->setMessage('Password retrieval', $user['password'])
217
                ->sendMessage();
218
            if ($mailer->isError()) {
219
                throw new Exception('Lost password : Email was not sent');
220
            }
221
        } catch (Exception $e) {
222
            $this->error = true;
223
            $this->errorMessage = $e->getMessage();
224
            $logger->error($e);
225
        }
226
        return $this->setRegistrationResponse(__CLASS__, __FUNCTION__);
227
    }
228
229
    /**
230
     * return true if request methods are allowed
231
     *
232
     * @return boolean
233
     */
234
    protected function isLoginMethodAllowed(): bool
235
    {
236
        return in_array(
237
            $this->request->getMethod(),
238
            [Request::METHOD_POST, Request::METHOD_TRACE]
239
        );
240
    }
241
242
    /**
243
     * return true if login action can be executed
244
     *
245
     * @param string $login
246
     * @param string $password
247
     * @return boolean
248
     */
249
    protected function isValidLogin(string $login, string $password): bool
250
    {
251
        return $this->isLoginMethodAllowed()
252
            && !empty($login)
253
            && !empty($password);
254
    }
255
256
    /**
257
     * return true if registration process can be executed
258
     *
259
     * @param string $login
260
     * @param string $password
261
     * @return boolean
262
     */
263
    protected function isValidRegistration(string $name, string $email, string $password): bool
264
    {
265
        $notEmpty = (!empty($name) && !empty($email) && !empty($password));
266
        $isMethodAllow = $this->request->getMethod() === Request::METHOD_POST;
267
        return $notEmpty && $isMethodAllow;
268
    }
269
270
    /**
271
     * return Auth and set response with http code and message
272
     *
273
     * @param integer $code
274
     * @param string $msg
275
     * @return Auth
276
     */
277
    protected function setErrorResponse(int $code, string $msg): Auth
278
    {
279
        $this->response
280
            ->setCode($code)
281
            ->setContent([
282
                Response::_ERROR => true,
283
                Response::_ERROR_CODE => $code,
284
                Response::_ERROR_MSG => $msg
285
            ])->getHeaderManager()->add(
286
                Headers::CONTENT_TYPE,
287
                'application/json'
288
            );
289
        return $this;
290
    }
291
292
    /**
293
     * set response with for a classname and action
294
     *
295
     * @param string $classname
296
     * @param string $action
297
     * @return Auth
298
     */
299
    protected function setRegistrationResponse(string $classname, string $action): Auth
300
    {
301
        $isError = $this->isError();
302
        $this->response
303
            ->setCode($this->getStatusCode())
304
            ->setContent(
305
                [
306
                    'error' => $this->error,
307
                    'errorMessage' => $this->errorMessage,
308
                    'datas' => [
309
                        'method' => $this->getRequest()->getMethod(),
310
                        'params' => $this->getParams(),
311
                        'controller' => $classname,
312
                        'action' => $action,
313
                        'query' => $isError
314
                            ? ''
315
                            : $this->sql,
316
                        'queryValues' => $isError
317
                            ? [] : $this->bindValues,
318
                        'rowset' => $isError
319
                            ? []
320
                            : $this->db->getRowset()
321
                    ]
322
                ]
323
            );
324
        return $this;
325
    }
326
327
    /**
328
     * returns true if error happened
329
     *
330
     * @return boolean
331
     */
332
    protected function isError(): bool
333
    {
334
        return $this->error === true;
335
    }
336
337
    /**
338
     * returns http status code
339
     *
340
     * @return int
341
     */
342
    protected function getStatusCode(): int
343
    {
344
        return (true === $this->isError())
345
            ? Response::HTTP_BAD_REQUEST
346
            : Response::HTTP_OK;
347
    }
348
}
349