Failed Conditions
Pull Request — master (#229)
by Rafael
02:55
created

AuthController::recover()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 18
ccs 0
cts 16
cp 0
crap 2
rs 9.6666
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\Validation as CanvasValidation;
24
use Canvas\Notifications\ResetPassword;
25
use Canvas\Notifications\PasswordUpdate;
26
use Canvas\Validations\PasswordValidation;
27
use Canvas\Traits\TokenTrait;
28
29
/**
30
 * Class AuthController.
31
 *
32
 * @package Canvas\Api\Controllers
33
 *
34
 * @property Users $userData
35
 * @property Request $request
36
 * @property Config $config
37
 * @property \Baka\Mail\Message $mail
38
 * @property Apps $app
39
 */
40
class AuthController extends \Baka\Auth\AuthController
41
{
42
    /**
43
     * Auth Trait.
44
     */
45
    use AuthTrait;
46
    use TokenTrait;
47
    use SocialLoginTrait;
48
49
    /**
50
     * Setup for this controller.
51
     *
52
     * @return void
53
     */
54
    public function onConstruct()
55
    {
56
        $this->userLinkedSourcesModel = new UserLinkedSources();
57
        $this->userModel = new Users();
58
59
        if (!isset($this->config->jwt)) {
60
            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...
61
        }
62
    }
63
64
    /**
65
     * User Login.
66
     * @method POST
67
     * @url /v1/auth
68
     *
69
     * @return Response
70
     */
71
    public function login() : Response
72
    {
73
        $request = $this->request->getPostData();
74
75
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
76
        $admin = 0;
77
        $remember = 1;
78
79
        //Ok let validate user password
80
        $validation = new CanvasValidation();
81
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid')]));
82
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
83
84
        $validation->setFilters('name', 'trim');
85
        $validation->setFilters('password', 'trim');
86
87
        //validate this form for password
88
        $validation->validate($request);
89
90
        $email = $validation->getValue('email');
91
        $password = $validation->getValue('password');
92
93
        /**
94
         * Login the user via ecosystem or app.
95
         */
96
        $auth = Factory::create($this->app->ecosystemAuth());
97
        $userData = $auth::login($email, $password, $remember, $admin, $userIp);
98
        $token = $userData->getToken();
99
100
        //start session
101
        $session = new Sessions();
102
        $session->start($userData, $token['sessionId'], $token['token'], $userIp, 1);
103
104
        return $this->response([
105
            'token' => $token['token'],
106
            'refresh_token' => $token['refresh_token'],
107
            'time' => date('Y-m-d H:i:s'),
108
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
109
            'refresh_token_expires' => date('Y-m-d H:i:s', time() + 31536000),
110
            'id' => $userData->getId()
111
        ]);
112
    }
113
114
    /**
115
     * User Signup.
116
     *
117
     * @method POST
118
     * @url /v1/users
119
     *
120
     * @return Response
121
     */
122
    public function signup() : Response
123
    {
124
        $user = $this->userModel;
125
126
        $request = $this->request->getPostData();
127
128
        //Ok let validate user password
129
        $validation = new CanvasValidation();
130
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
131
        $validation->add('firstname', new PresenceOf(['message' => _('The firstname is required.')]));
132
        $validation->add('lastname', new PresenceOf(['message' => _('The lastname is required.')]));
133
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid.')]));
134
135
        $validation->add(
136
            'password',
137
            new StringLength([
138
                'min' => 8,
139
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
140
            ])
141
        );
142
143
        $validation->add('password', new Confirmation([
144
            'message' => _('Password and confirmation do not match.'),
145
            'with' => 'verify_password',
146
        ]));
147
148
        $validation->setFilters('password', 'trim');
149
        $validation->setFilters('displayname', 'trim');
150
        $validation->setFilters('default_company', 'trim');
151
152
        //validate this form for password
153
        $validation->validate($request);
154
155
        $user->email = $validation->getValue('email');
156
        $user->firstname = $validation->getValue('firstname');
157
        $user->lastname = $validation->getValue('lastname');
158
        $user->password = $validation->getValue('password');
159
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
160
        $user->displayname = $validation->getValue('displayname');
161
        $user->defaultCompanyName = $validation->getValue('default_company');
162
163
        //user registration
164
        try {
165
            $this->db->begin();
166
167
            $user->signUp();
168
169
            $this->db->commit();
170
        } catch (Exception $e) {
171
            $this->db->rollback();
172
173
            throw new Exception($e->getMessage());
174
        }
175
176
        $token = $user->getToken();
177
178
        //start session
179
        $session = new Sessions();
180
        $session->start($user, $token['sessionId'], $token['token'], $userIp, 1);
181
182
        $authSession = [
183
            'token' => $token['token'],
184
            'time' => date('Y-m-d H:i:s'),
185
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
186
            'id' => $user->getId(),
187
        ];
188
189
        $user->password = null;
190
        $this->sendEmail($user, 'signup');
191
192
        return $this->response([
193
            'user' => $user,
194
            'session' => $authSession
195
        ]);
196
    }
197
198
    /**
199
     * Refresh user auth.
200
     *
201
     * @return Response
202
     * @todo Validate acces_token and refresh token, session's user email and relogin
203
     */
204
    public function refresh(): Response
205
    {
206
        $request = $this->request->getPostData();
207
        $accessToken = $this->getToken($request['access_token']);
208
        $refreshToken = $this->getToken($request['refresh_token']);
209
        $user = null;
210
211
        if (time() != $accessToken->getClaim('exp')) {
212
            throw new ServerErrorHttpException('Issued Access Token has not expired');
1 ignored issue
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...
213
        }
214
215
        //Check if both tokens relate to the same user's email
216
        if ($accessToken->getClaim('sessionId') == $refreshToken->getClaim('sessionId')) {
217
            $user = Users::getByEmail($accessToken->getClaim('email'));
218
        }
219
220
        $token = Sessions::restart($user, $refreshToken->getClaim('sessionId'), (string)$this->request->getClientAddress());
221
222
        return $this->response([
223
            'token' => $token['token'],
224
            'time' => date('Y-m-d H:i:s'),
225
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
226
            'id' => $user->getId(),
227
        ]);
228
    }
229
230
    /**
231
     * Send email to change current email for user.
232
     * @param int $id
233
     * @return Response
234
     */
235
    public function sendEmailChange(int $id): Response
236
    {
237
        //Search for user
238
        $user = Users::getById($id);
239
240
        if (!is_object($user)) {
241
            throw new NotFoundHttpException(_('User not found'));
242
        }
243
244
        //Send email
245
        $this->sendEmail($user, 'email-change');
246
247
        return $this->response($user);
248
    }
249
250
    /**
251
     * Change user's email.
252
     * @param string $hash
253
     * @return Response
254
     */
255
    public function changeUserEmail(string $hash): Response
256
    {
257
        $request = $this->request->getPostData();
258
259
        //Ok let validate user password
260
        $validation = new CanvasValidation();
261
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
262
        $validation->add('new_email', new EmailValidator(['message' => _('The email is not valid.')]));
263
264
        $validation->add(
265
            'password',
266
            new StringLength([
267
                'min' => 8,
268
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
269
            ])
270
        );
271
272
        //validate this form for password
273
        $validation->setFilters('password', 'trim');
274
        $validation->setFilters('default_company', 'trim');
275
        $validation->validate($request);
276
277
        $newEmail = $validation->getValue('new_email');
278
        $password = $validation->getValue('password');
279
280
        //Search user by key
281
        $user = Users::getByUserActivationEmail($hash);
282
283
        if (!is_object($user)) {
284
            throw new NotFoundHttpException(_('User not found'));
285
        }
286
287
        $this->db->begin();
288
289
        $user->email = $newEmail;
290
291
        if (!$user->update()) {
292
            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...
293
        }
294
295
        if (!$userData = $this->loginUsers($user->email, $password)) {
296
            $this->db->rollback();
297
        }
298
299
        $this->db->commit();
300
301
        return $this->response($userData);
302
    }
303
304
    /**
305
     * Login user using Access Token.
306
     * @return Response
307
     */
308
    public function loginBySocial(): Response
309
    {
310
        $request = $this->request->getPostData();
311
312
        $source = Sources::findFirstOrFail([
313
            'title = ?0 and is_deleted = 0',
314
            'bind' => [$request['provider']]
315
        ]);
316
317
        return $this->response($this->providerLogin($source, $request['social_id'], $request));
318
    }
319
320
    /**
321
     * Send the user how filled out the form to the specify email
322
     * a link to reset his password.
323
     *
324
     * @return Response
325
     */
326
    public function recover(): Response
327
    {
328
        $request = $this->request->getPostData();
329
330
        $validation = new CanvasValidation();
331
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid.')]));
332
333
        $validation->validate($request);
334
335
        $email = $validation->getValue('email');
336
337
        $recoverUser = Users::getByEmail($email);
338
        $recoverUser->generateForgotHash();
339
340
        $recoverUser->notify(new ResetPassword($recoverUser));
341
342
        return $this->response(_('Check your email to recover your password'));
343
    }
344
345
    /**
346
     * Reset the user password.
347
     * @method PUT
348
     * @url /v1/reset
349
     *
350
     * @return Response
351
     */
352
    public function reset(string $key) : Response
353
    {
354
        //is the key empty or does it existe?
355
        if (empty($key) || !$userData = Users::findFirst(['user_activation_forgot = :key:', 'bind' => ['key' => $key]])) {
356
            throw new Exception(_('This Key to reset password doesn\'t exist'));
357
        }
358
359
        $request = $this->request->getPostData();
360
361
        // Get the new password and the verify
362
        $newPassword = trim($request['new_password']);
363
        $verifyPassword = trim($request['verify_password']);
364
365
        //Ok let validate user password
366
        PasswordValidation::validate($newPassword, $verifyPassword);
367
368
        // Has the password and set it
369
        $userData->resetPassword($newPassword);
370
        $userData->user_activation_forgot = '';
371
        $userData->updateOrFail();
372
373
        //log the user out of the site from all devices
374
        $session = new Sessions();
375
        $session->end($userData);
376
377
        $userData->notify(new PasswordUpdate($userData));
378
379
        return $this->response(_('Password Updated'));
380
    }
381
382
    /**
383
    * Set the email config array we are going to be sending.
384
    *
385
    * @todo deprecated move to notifications
386
    * @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...
387
    * @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...
388
    * @return void
389
    */
390
    protected function sendEmail(BakaUsers $user, string $type): void
391
    {
392
        $send = true;
393
        $subject = null;
394
        $body = null;
395
        switch ($type) {
396
            case 'recover':
397
                $recoveryLink = $this->config->app->frontEndUrl . '/users/reset-password/' . $user->user_activation_forgot;
398
                $subject = _('Password Recovery');
399
                $body = sprintf(_('Click %shere%s to set a new password for your account.'), '<a href="' . $recoveryLink . '" target="_blank">', '</a>');
400
                // send email to recover password
401
                break;
402
            case 'reset':
403
                $activationUrl = $this->config->app->frontEndUrl . '/user/activate/' . $user->user_activation_key;
404
                $subject = _('Password Updated!');
405
                $body = sprintf(_('Your password was update please, use this link to activate your account: %sActivate account%s'), '<a href="' . $activationUrl . '">', '</a>');
406
                // send email that password was update
407
                break;
408
            case 'email-change':
409
                $emailChangeUrl = $this->config->app->frontEndUrl . '/user/' . $user->user_activation_email . '/email';
410
                $subject = _('Email Change Request');
411
                $body = sprintf(_('Click %shere%s to set a new email for your account.'), '<a href="' . $emailChangeUrl . '">', '</a>');
412
                break;
413
            default:
414
                $send = false;
415
                break;
416
        }
417
418
        if ($send) {
419
            $this->mail
420
            ->to($user->email)
421
            ->subject($subject)
422
            ->content($body)
423
            ->sendNow();
424
        }
425
    }
426
}
427