Test Failed
Pull Request — master (#163)
by Rafael
06:40
created

AuthController::sendResetEmail()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 6
ccs 0
cts 3
cp 0
crap 2
rs 10
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;
18
use Phalcon\Validation\Validator\Confirmation;
19
use Phalcon\Validation\Validator\Email as EmailValidator;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\Email was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use Phalcon\Validation\Validator\PresenceOf;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\PresenceOf was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Phalcon\Validation\Validator\StringLength;
0 ignored issues
show
Bug introduced by
The type Phalcon\Validation\Validator\StringLength was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use Baka\Auth\Models\Sessions;
23
use Canvas\Auth\Factory;
24
use Canvas\Validation as CanvasValidation;
25
use Canvas\Notifications\ResetPassword;
26
27
/**
28
 * Class AuthController.
29
 *
30
 * @package Canvas\Api\Controllers
31
 *
32
 * @property Users $userData
33
 * @property Request $request
34
 * @property Config $config
35
 * @property \Baka\Mail\Message $mail
36
 * @property Apps $app
37
 */
38
class AuthController extends \Baka\Auth\AuthController
39
{
40
    /**
41
     * Auth Trait.
42
     */
43
    use AuthTrait;
44
    use SocialLoginTrait;
45
46
    /**
47
     * Setup for this controller.
48
     *
49
     * @return void
50
     */
51
    public function onConstruct()
52
    {
53
        $this->userLinkedSourcesModel = new UserLinkedSources();
54
        $this->userModel = new Users();
55
56
        if (!isset($this->config->jwt)) {
57
            throw new ServerErrorHttpException('You need to configure your app JWT');
58
        }
59
    }
60
61
    /**
62
     * User Login.
63
     * @method POST
64
     * @url /v1/auth
65
     *
66
     * @return Response
67
     */
68
    public function login() : Response
69
    {
70
        $email = trim($this->request->getPost('email', 'string', ''));
71
        $password = trim($this->request->getPost('password', 'string', ''));
72
        $admin = 0;
73
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
74
        $remember = $this->request->getPost('remember', 'int', 1);
75
76
        //Ok let validate user password
77
        $validation = new CanvasValidation();
78
        $validation->add('email', new PresenceOf(['message' => _('The email is required.')]));
79
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
80
81
        //validate this form for password
82
        $validation->validate($this->request->getPost());
83
84
        /**
85
         * Login the user via ecosystem or app.
86
         */
87
        $auth = Factory::create($this->app->ecosystemAuth());
88
        $userData = $auth::login($email, $password, $remember, $admin, $userIp);
0 ignored issues
show
Bug introduced by
The method login() does not exist on Canvas\Auth\Auth. Since it exists in all sub-types, consider adding an abstract or default implementation to Canvas\Auth\Auth. ( Ignorable by Annotation )

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

88
        /** @scrutinizer ignore-call */ 
89
        $userData = $auth::login($email, $password, $remember, $admin, $userIp);
Loading history...
89
        $token = $userData->getToken();
90
91
        //start session
92
        $session = new Sessions();
93
        $session->start($userData, $token['sessionId'], $token['token'], $userIp, 1);
94
95
        return $this->response([
96
            'token' => $token['token'],
97
            'time' => date('Y-m-d H:i:s'),
98
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
99
            'id' => $userData->getId(),
100
        ]);
101
    }
102
103
    /**
104
     * User Signup.
105
     *
106
     * @method POST
107
     * @url /v1/users
108
     *
109
     * @return Response
110
     */
111
    public function signup() : Response
112
    {
113
        $user = $this->userModel;
114
115
        $user->email = $this->request->getPost('email', 'email');
116
        $user->firstname = ltrim(trim($this->request->getPost('firstname', 'string', '')));
117
        $user->lastname = ltrim(trim($this->request->getPost('lastname', 'string', '')));
118
        $user->password = ltrim(trim($this->request->getPost('password', 'string', '')));
119
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1'; //help getting the client ip on scrutinizer :(
120
        $user->displayname = ltrim(trim($this->request->getPost('displayname', 'string', '')));
121
        $user->defaultCompanyName = ltrim(trim($this->request->getPost('default_company', 'string', '')));
122
123
        //Ok let validate user password
124
        $validation = new CanvasValidation();
125
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
126
        $validation->add('firstname', new PresenceOf(['message' => _('The firstname is required.')]));
127
        $validation->add('email', new EmailValidator(['message' => _('The email is not valid.')]));
128
129
        $validation->add(
130
            'password',
131
            new StringLength([
132
                'min' => 8,
133
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
134
            ])
135
        );
136
137
        $validation->add('password', new Confirmation([
138
            'message' => _('Password and confirmation do not match.'),
139
            'with' => 'verify_password',
140
        ]));
141
142
        //validate this form for password
143
        $validation->validate($this->request->getPost());
144
145
        //user registration
146
        try {
147
            $this->db->begin();
148
149
            $user->signup();
150
151
            $this->db->commit();
152
        } catch (Exception $e) {
153
            $this->db->rollback();
154
155
            throw new Exception($e->getMessage());
156
        }
157
158
        $token = $user->getToken();
159
160
        //start session
161
        $session = new Sessions();
162
        $session->start($user, $token['sessionId'], $token['token'], $userIp, 1);
163
164
        $authSession = [
165
            'token' => $token['token'],
166
            'time' => date('Y-m-d H:i:s'),
167
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
168
            'id' => $user->getId(),
169
        ];
170
171
        $user->password = null;
172
        $this->sendEmail($user, 'signup');
173
174
        return $this->response([
175
            'user' => $user,
176
            'session' => $authSession
177
        ]);
178
    }
179
180
    /**
181
     * Send email to change current email for user.
182
     * @param int $id
183
     * @return Response
184
     */
185
    public function sendEmailChange(int $id): Response
186
    {
187
        //Search for user
188
        $user = Users::getById($id);
189
190
        if (!is_object($user)) {
191
            throw new NotFoundHttpException(_('User not found'));
0 ignored issues
show
Bug introduced by
The type Canvas\Api\Controllers\NotFoundHttpException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
192
        }
193
194
        //Send email
195
        $this->sendEmail($user, 'email-change');
196
197
        return $this->response($user);
198
    }
199
200
    /**
201
    * Set the email config array we are going to be sending.
202
    *
203
    * @param String $emailAction
204
    * @param Users  $user
205
    * @return void
206
    */
207
    protected function sendEmail(BakaUsers $user, string $type): void
208
    {
209
        $send = true;
210
        $subject = null;
211
        $body = null;
212
        switch ($type) {
213
            case 'recover':
214
                $recoveryLink = $this->config->app->frontEndUrl . '/users/reset-password/' . $user->user_activation_forgot;
215
                $subject = _('Password Recovery');
216
                $body = sprintf(_('Click %shere%s to set a new password for your account.'), '<a href="' . $recoveryLink . '" target="_blank">', '</a>');
217
                // send email to recover password
218
                break;
219
            case 'reset':
220
                $activationUrl = $this->config->app->frontEndUrl . '/user/activate/' . $user->user_activation_key;
221
                $subject = _('Password Updated!');
222
                $body = sprintf(_('Your password was update please, use this link to activate your account: %sActivate account%s'), '<a href="' . $activationUrl . '">', '</a>');
223
                // send email that password was update
224
                break;
225
            case 'email-change':
226
                $emailChangeUrl = $this->config->app->frontEndUrl . '/user/' . $user->user_activation_email . '/email';
227
                $subject = _('Email Change Request');
228
                $body = sprintf(_('Click %shere%s to set a new email for your account.'), '<a href="' . $emailChangeUrl . '">', '</a>');
229
                break;
230
            default:
231
                $send = false;
232
                break;
233
        }
234
235
        if ($send) {
236
            $this->mail
237
            ->to($user->email)
238
            ->subject($subject)
239
            ->content($body)
240
            ->sendNow();
241
        }
242
    }
243
244
    /**
245
     * Change user's email.
246
     * @param string $hash
247
     * @return Response
248
     */
249
    public function changeUserEmail(string $hash): Response
250
    {
251
        $newEmail = $this->request->getPost('new_email', 'string');
252
        $password = $this->request->getPost('password', 'string');
253
254
        //Search user by key
255
        $user = Users::getByUserActivationEmail($hash);
256
257
        if (!is_object($user)) {
258
            throw new NotFoundHttpException(_('User not found'));
259
        }
260
261
        $this->db->begin();
262
263
        $user->email = $newEmail;
264
265
        if (!$user->update()) {
266
            throw new ModelException((string)current($user->getMessages()));
267
        }
268
269
        if (!$userData = $this->loginUsers($user->email, $password)) {
270
            $this->db->rollback();
271
        }
272
273
        $this->db->commit();
274
275
        return $this->response($userData);
276
    }
277
278
    /**
279
     * Login user using Access Token.
280
     * @return Response
281
     */
282
    public function loginBySocial(): Response
283
    {
284
        $request = $this->request->getPostData();
285
286
        $source = Sources::findFirstOrFail([
287
            'title = ?0 and is_deleted = 0',
288
            'bind' => [$request['provider']]
289
        ]);
290
291
        return $this->response($this->providerLogin($source, $request['social_id'], $request['email']));
292
    }
293
294
    /**
295
     * Send reset email to user
296
     * @param string $email
297
     * @return void
298
     */
299
    public function sendResetEmail(): void
300
    {
301
        /**
302
         * Lets notify the current user about its password reset and give a link to change it. Frontend must have an endpoint called /user/reset/{key}.
303
         */
304
        $this->userData->notify(new ResetPassword($this->userData));
305
    }
306
307
    /**
308
     * Reset the user password.
309
     * @method PUT
310
     * @url /v1/reset
311
     *
312
     * @return Response
313
     */
314
    public function processReset(string $key) : Response
315
    {
316
        //is the key empty or does it existe?
317
        if (empty($key) || !$userData = Users::findFirst(['user_activation_forgot = :key:', 'bind' => ['key' => $key]])) {
318
            throw new Exception(_('This Key to reset password doesn\'t exist'));
319
        }
320
321
        $request = $this->request->getPost();
322
323
        if (isset($request['new_password']) && (!empty($request['new_password']) && !empty($request['current_password']))) {
324
            //Ok let validate user password
325
            $validation = new Validation();
326
            $validation->add('new_password', new PresenceOf(['message' => 'The new_password is required.']));
327
            $validation->add('current_password', new PresenceOf(['message' => 'The current_password is required.']));
328
            $validation->add('confirm_new_password', new PresenceOf(['message' => 'The confirm_new_password is required.']));
329
            $messages = $validation->validate($request);
330
            if (count($messages)) {
331
                foreach ($messages as $message) {
332
                    throw new BadRequestHttpException((string)$message);
0 ignored issues
show
Bug introduced by
The type Canvas\Api\Controllers\BadRequestHttpException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
333
                }
334
            }
335
            $userData->updatePassword($request['current_password'], $request['new_password'], $request['confirm_new_password']);
336
337
            //Lets create a new user_activation_forgot
338
            $userData->user_activation_forgot = $userData->generateActivationKey();
339
            //log the user out of the site from all devices
340
            if ($userData->update()) {
341
                $session = new Sessions();
342
                $session->end($userData);
343
            }
344
345
            return $this->response(_('Congratulations! You\'ve successfully changed your password.'));
346
        } else {
347
            //remove on any actino that doesnt involve password
348
            unset($request['password']);
349
            throw new Exception(_('Password are not the same'));
350
        }
351
    }
352
}
353