Failed Conditions
Push — master ( 6e6187...d85785 )
by Maximo
03:12 queued 37s
created

AuthController::refresh()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 0
dl 0
loc 29
ccs 0
cts 18
cp 0
crap 20
rs 9.456
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Canvas\Api\Controllers;
6
7
use Canvas\Models\Users;
8
use Canvas\Models\Sources;
9
use Canvas\Models\UserLinkedSources;
10
use Canvas\Exception\ServerErrorHttpException;
11
use Canvas\Exception\ModelException;
12
use Baka\Auth\Models\Users as BakaUsers;
13
use Canvas\Traits\AuthTrait;
14
use Canvas\Traits\SocialLoginTrait;
15
use Exception;
16
use Phalcon\Http\Response;
17
use Phalcon\Validation\Validator\Confirmation;
18
use Phalcon\Validation\Validator\Email as EmailValidator;
19
use Phalcon\Validation\Validator\PresenceOf;
20
use Phalcon\Validation\Validator\StringLength;
21
use Baka\Auth\Models\Sessions;
22
use Canvas\Auth\Factory;
23
use Canvas\Exception\NotFoundException;
24
use Canvas\Validation as CanvasValidation;
25
use Canvas\Notifications\ResetPassword;
26
use Canvas\Notifications\PasswordUpdate;
27
use Canvas\Validations\PasswordValidation;
28
use Canvas\Traits\TokenTrait;
29
30
/**
31
 * Class AuthController.
32
 *
33
 * @package Canvas\Api\Controllers
34
 *
35
 * @property Users $userData
36
 * @property Request $request
37
 * @property Config $config
38
 * @property \Baka\Mail\Message $mail
39
 * @property Apps $app
40
 */
41
class AuthController extends \Baka\Auth\AuthController
42
{
43
    /**
44
     * Auth Trait.
45
     */
46
    use AuthTrait;
47
    use TokenTrait;
48
    use SocialLoginTrait;
49
50
    /**
51
     * Setup for this controller.
52
     *
53
     * @return void
54
     */
55
    public function onConstruct()
56
    {
57
        $this->userLinkedSourcesModel = new UserLinkedSources();
58
        $this->userModel = new Users();
59
60
        if (!isset($this->config->jwt)) {
61
            throw new ServerErrorHttpException('You need to configure your app JWT');
0 ignored issues
show
Deprecated Code introduced by
The class Canvas\Exception\ServerErrorHttpException has been deprecated with message: version 0.1.5

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
62
        }
63
    }
64
65
    /**
66
     * User Login.
67
     * @method POST
68
     * @url /v1/auth
69
     *
70
     * @return Response
71
     */
72
    public function login() : Response
73
    {
74
        $request = $this->request->getPostData();
75
76
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
77
        $admin = 0;
78
        $remember = 1;
79
80
        //Ok let validate user password
81
        $validation = new CanvasValidation();
82
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid')]));
83
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
84
85
        $validation->setFilters('name', 'trim');
86
        $validation->setFilters('password', 'trim');
87
88
        //validate this form for password
89
        $validation->validate($request);
90
91
        $email = $validation->getValue('email');
92
        $password = $validation->getValue('password');
93
94
        /**
95
         * Login the user via ecosystem or app.
96
         */
97
        $auth = Factory::create($this->app->ecosystemAuth());
98
        $userData = $auth::login($email, $password, $remember, $admin, $userIp);
99
        $token = $userData->getToken();
100
101
        //start session
102
        $session = new Sessions();
103
        $session->start($userData, $token['sessionId'], $token['token'], $userIp, 1);
104
105
        return $this->response([
106
            'token' => $token['token'],
107
            'refresh_token' => $token['refresh_token'],
108
            'time' => date('Y-m-d H:i:s'),
109
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
110
            'refresh_token_expires' => date('Y-m-d H:i:s', time() + 31536000),
111
            'id' => $userData->getId()
112
        ]);
113
    }
114
115
    /**
116
     * User Signup.
117
     *
118
     * @method POST
119
     * @url /v1/users
120
     *
121
     * @return Response
122
     */
123
    public function signup() : Response
124
    {
125
        $user = $this->userModel;
126
127
        $request = $this->request->getPostData();
128
129
        //Ok let validate user password
130
        $validation = new CanvasValidation();
131
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
132
        $validation->add('firstname', new PresenceOf(['message' => _('The firstname is required.')]));
133
        $validation->add('lastname', new PresenceOf(['message' => _('The lastname is required.')]));
134
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid.')]));
135
136
        $validation->add(
137
            'password',
138
            new StringLength([
139
                'min' => 8,
140
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
141
            ])
142
        );
143
144
        $validation->add('password', new Confirmation([
145
            'message' => _('Password and confirmation do not match.'),
146
            'with' => 'verify_password',
147
        ]));
148
149
        $validation->setFilters('password', 'trim');
150
        $validation->setFilters('displayname', 'trim');
151
        $validation->setFilters('default_company', 'trim');
152
153
        //validate this form for password
154
        $validation->validate($request);
155
156
        $user->email = $validation->getValue('email');
157
        $user->firstname = $validation->getValue('firstname');
158
        $user->lastname = $validation->getValue('lastname');
159
        $user->password = $validation->getValue('password');
160
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
161
        $user->displayname = $validation->getValue('displayname');
162
        $user->defaultCompanyName = $validation->getValue('default_company');
163
164
        //user registration
165
        try {
166
            $this->db->begin();
167
168
            $user->signUp();
169
170
            $this->db->commit();
171
        } catch (Exception $e) {
172
            $this->db->rollback();
173
174
            throw new Exception($e->getMessage());
175
        }
176
177
        $token = $user->getToken();
178
179
        //start session
180
        $session = new Sessions();
181
        $session->start($user, $token['sessionId'], $token['token'], $userIp, 1);
182
183
        $authSession = [
184
            'token' => $token['token'],
185
            'time' => date('Y-m-d H:i:s'),
186
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
187
            'id' => $user->getId(),
188
        ];
189
190
        $user->password = null;
191
        $this->sendEmail($user, 'signup');
192
193
        return $this->response([
194
            'user' => $user,
195
            'session' => $authSession
196
        ]);
197
    }
198
199
    /**
200
     * Refresh user auth.
201
     *
202
     * @return Response
203
     * @todo Validate acces_token and refresh token, session's user email and relogin
204
     */
205
    public function refresh(): Response
206
    {
207
        $request = $this->request->getPostData();
208
        $accessToken = $this->getToken($request['access_token']);
209
        $refreshToken = $this->getToken($request['refresh_token']);
210
        $user = null;
211
212
        if (time() != $accessToken->getClaim('exp')) {
213
            throw new ServerErrorHttpException('Issued Access Token has not expired');
214
        }
215
216
        //Check if both tokens relate to the same user's email
217
        if ($accessToken->getClaim('sessionId') == $refreshToken->getClaim('sessionId')) {
218
            $user = Users::getByEmail($accessToken->getClaim('email'));
219
        }
220
221
        if (!$user) {
222
            throw new NotFoundException(_('User not found'));
0 ignored issues
show
Deprecated Code introduced by
The class Canvas\Exception\NotFoundException has been deprecated with message: version 0.1.5

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
223
        }
224
225
        $token = Sessions::restart($user, $refreshToken->getClaim('sessionId'), (string)$this->request->getClientAddress());
226
227
        return $this->response([
228
            'token' => $token['token'],
229
            'time' => date('Y-m-d H:i:s'),
230
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
231
            'id' => $user->getId(),
232
        ]);
233
    }
234
235
    /**
236
     * Send email to change current email for user.
237
     * @param int $id
238
     * @return Response
239
     */
240
    public function sendEmailChange(int $id): Response
241
    {
242
        //Search for user
243
        $user = Users::getById($id);
244
245
        if (!is_object($user)) {
246
            throw new NotFoundHttpException(_('User not found'));
247
        }
248
249
        //Send email
250
        $this->sendEmail($user, 'email-change');
251
252
        return $this->response($user);
253
    }
254
255
    /**
256
     * Change user's email.
257
     * @param string $hash
258
     * @return Response
259
     */
260
    public function changeUserEmail(string $hash): Response
261
    {
262
        $request = $this->request->getPostData();
263
264
        //Ok let validate user password
265
        $validation = new CanvasValidation();
266
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
267
        $validation->add('new_email', new EmailValidator(['message' => _('The email is not valid.')]));
268
269
        $validation->add(
270
            'password',
271
            new StringLength([
272
                'min' => 8,
273
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
274
            ])
275
        );
276
277
        //validate this form for password
278
        $validation->setFilters('password', 'trim');
279
        $validation->setFilters('default_company', 'trim');
280
        $validation->validate($request);
281
282
        $newEmail = $validation->getValue('new_email');
283
        $password = $validation->getValue('password');
284
285
        //Search user by key
286
        $user = Users::getByUserActivationEmail($hash);
287
288
        if (!is_object($user)) {
289
            throw new NotFoundHttpException(_('User not found'));
290
        }
291
292
        $this->db->begin();
293
294
        $user->email = $newEmail;
295
296
        if (!$user->update()) {
297
            throw new ModelException((string)current($user->getMessages()));
0 ignored issues
show
Deprecated Code introduced by
The class Canvas\Exception\ModelException has been deprecated with message: version 0.1.5

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
298
        }
299
300
        if (!$userData = $this->loginUsers($user->email, $password)) {
301
            $this->db->rollback();
302
        }
303
304
        $this->db->commit();
305
306
        return $this->response($userData);
307
    }
308
309
    /**
310
     * Login user using Access Token.
311
     * @return Response
312
     */
313
    public function loginBySocial(): Response
314
    {
315
        $request = $this->request->getPostData();
316
317
        $source = Sources::findFirstOrFail([
318
            'title = ?0 and is_deleted = 0',
319
            'bind' => [$request['provider']]
320
        ]);
321
322
        return $this->response($this->providerLogin($source, $request['social_id'], $request));
323
    }
324
325
    /**
326
     * Send the user how filled out the form to the specify email
327
     * a link to reset his password.
328
     *
329
     * @return Response
330
     */
331
    public function recover(): Response
332
    {
333
        $request = $this->request->getPostData();
334
335
        $validation = new CanvasValidation();
336
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid.')]));
337
338
        $validation->validate($request);
339
340
        $email = $validation->getValue('email');
341
342
        $recoverUser = Users::getByEmail($email);
343
        $recoverUser->generateForgotHash();
344
345
        $recoverUser->notify(new ResetPassword($recoverUser));
346
347
        return $this->response(_('Check your email to recover your password'));
348
    }
349
350
    /**
351
     * Reset the user password.
352
     * @method PUT
353
     * @url /v1/reset
354
     *
355
     * @return Response
356
     */
357
    public function reset(string $key) : Response
358
    {
359
        //is the key empty or does it existe?
360
        if (empty($key) || !$userData = Users::findFirst(['user_activation_forgot = :key:', 'bind' => ['key' => $key]])) {
361
            throw new Exception(_('This Key to reset password doesn\'t exist'));
362
        }
363
364
        $request = $this->request->getPostData();
365
366
        // Get the new password and the verify
367
        $newPassword = trim($request['new_password']);
368
        $verifyPassword = trim($request['verify_password']);
369
370
        //Ok let validate user password
371
        PasswordValidation::validate($newPassword, $verifyPassword);
372
373
        // Has the password and set it
374
        $userData->resetPassword($newPassword);
375
        $userData->user_activation_forgot = '';
376
        $userData->updateOrFail();
377
378
        //log the user out of the site from all devices
379
        $session = new Sessions();
380
        $session->end($userData);
381
382
        $userData->notify(new PasswordUpdate($userData));
383
384
        return $this->response(_('Password Updated'));
385
    }
386
387
    /**
388
    * Set the email config array we are going to be sending.
389
    *
390
    * @todo deprecated move to notifications
391
    * @param String $emailAction
0 ignored issues
show
Bug introduced by
There is no parameter named $emailAction. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
392
    * @param Users  $user
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $user a bit more specific; maybe use BakaUsers.
Loading history...
393
    * @return void
394
    */
395
    protected function sendEmail(BakaUsers $user, string $type): void
396
    {
397
        $send = true;
398
        $subject = null;
399
        $body = null;
400
        switch ($type) {
401
            case 'recover':
402
                $recoveryLink = $this->config->app->frontEndUrl . '/users/reset-password/' . $user->user_activation_forgot;
403
                $subject = _('Password Recovery');
404
                $body = sprintf(_('Click %shere%s to set a new password for your account.'), '<a href="' . $recoveryLink . '" target="_blank">', '</a>');
405
                // send email to recover password
406
                break;
407
            case 'reset':
408
                $activationUrl = $this->config->app->frontEndUrl . '/user/activate/' . $user->user_activation_key;
409
                $subject = _('Password Updated!');
410
                $body = sprintf(_('Your password was update please, use this link to activate your account: %sActivate account%s'), '<a href="' . $activationUrl . '">', '</a>');
411
                // send email that password was update
412
                break;
413
            case 'email-change':
414
                $emailChangeUrl = $this->config->app->frontEndUrl . '/user/' . $user->user_activation_email . '/email';
415
                $subject = _('Email Change Request');
416
                $body = sprintf(_('Click %shere%s to set a new email for your account.'), '<a href="' . $emailChangeUrl . '">', '</a>');
417
                break;
418
            default:
419
                $send = false;
420
                break;
421
        }
422
423
        if ($send) {
424
            $this->mail
425
            ->to($user->email)
426
            ->subject($subject)
427
            ->content($body)
428
            ->sendNow();
429
        }
430
    }
431
}
432