Passed
Pull Request — master (#37)
by Maximo
08:12 queued 01:23
created

UsersInviteController::sendInviteEmail()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.125

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 4
nop 2
dl 0
loc 20
ccs 7
cts 14
cp 0.5
crap 4.125
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gewaer\Api\Controllers;
6
7
use Gewaer\Models\UsersInvite;
8
use Gewaer\Models\Users;
9
use Gewaer\Models\UsersAssociatedCompany;
10
use Gewaer\Models\Roles;
11
use Phalcon\Security\Random;
12
use Phalcon\Validation;
13
use Phalcon\Validation\Validator\PresenceOf;
14
use Phalcon\Validation\Validator\StringLength;
15
use Gewaer\Exception\UnprocessableEntityHttpException;
16
use Gewaer\Exception\NotFoundHttpException;
17
use Gewaer\Exception\ServerErrorHttpException;
18
use Phalcon\Http\Response;
19
use Exception;
20
use Baka\Auth\Models\Sessions;
21
use Gewaer\Exception\ModelException;
22
23
/**
24
 * Class LanguagesController
25
 * @property Users $userData
26
 * @property Request $request
27
 * @property Config $config
28
 * @property Apps $app
29
 * @property Mail $mail
30
 * @property Auth $auth
31
 * @package Gewaer\Api\Controllers
32
 *
33
 */
34
class UsersInviteController extends BaseController
35
{
36
    /*
37
     * fields we accept to create
38
     *
39
     * @var array
40
     */
41
    protected $createFields = ['invite_hash', 'companies_id', 'role_id', 'app_id', 'email'];
42
43
    /*
44
     * fields we accept to create
45
     *
46
     * @var array
47
     */
48
    protected $updateFields = ['invite_hash', 'companies_id', 'role_id', 'app_id', 'email'];
49
50
    /**
51
     * set objects
52
     *
53
     * @return void
54
     */
55 4
    public function onConstruct()
56
    {
57 4
        $this->model = new UsersInvite();
58 4
        $this->additionalSearchFields = [
59 4
            ['is_deleted', ':', '0'],
60 4
            ['companies_id', ':', $this->userData->currentCompanyId()],
61
        ];
62 4
    }
63
64
    /**
65
     * Get users invite by hash
66
     * @param string $hash
67
     * @return Response
68
     */
69 1
    public function getByHash(string $hash):Response
70
    {
71 1
        $userInvite = $this->model::findFirst([
72 1
            'conditions' => 'invite_hash =  ?0 and is_deleted = 0',
73 1
            'bind' => [$hash]
74
        ]);
75
76 1
        if (!is_object($userInvite)) {
77
            throw new NotFoundHttpException('Users Invite not found');
78
        }
79
80 1
        return $this->response($userInvite);
81
    }
82
83
    /**
84
     * Sets up invitation information for a would be user
85
     * @return Response
86
     */
87 4
    public function insertInvite(): Response
88
    {
89 4
        $request = $this->request->getPost();
90 4
        $random = new Random();
91
92 4
        $validation = new Validation();
93 4
        $validation->add('email', new PresenceOf(['message' => _('The email is required.')]));
94 4
        $validation->add('role_id', new PresenceOf(['message' => _('The role is required.')]));
95
96
        //validate this form for password
97 4
        $messages = $validation->validate($this->request->getPost());
98 4
        if (count($messages)) {
99
            foreach ($messages as $message) {
100
                throw new ServerErrorHttpException((string)$message);
101
            }
102
        }
103
104
        //Check if user was already was invited to current company and return message
105 4
        $invitedUser = $this->model::findFirst([
106 4
            'conditions' => 'email = ?0 and companies_id = ?1 and role_id = ?2',
107 4
            'bind' => [$request['email'], $this->userData->default_company, $request['role_id']]
108
        ]);
109
110 4
        if (is_object($invitedUser)) {
111
            throw new ModelException('User already invited to this company and added with this role');
112
        }
113
114
        //Save data to users_invite table and generate a hash for the invite
115 4
        $userInvite = $this->model;
116 4
        $userInvite->companies_id = $this->userData->default_company;
117 4
        $userInvite->app_id = $this->app->getId();
118 4
        $userInvite->role_id = Roles::getById((int)$request['role_id'])->id;
119 4
        $userInvite->email = $request['email'];
120 4
        $userInvite->invite_hash = $random->base58();
121 4
        $userInvite->created_at = date('Y-m-d H:m:s');
122
123 4
        if (!$userInvite->save()) {
124
            throw new UnprocessableEntityHttpException((string) current($userInvite->getMessages()));
125
        }
126
127 4
        $this->sendInviteEmail($request['email'], $userInvite->invite_hash);
128 4
        return $this->response($userInvite);
129
    }
130
131
    /**
132
     * Send users invite email
133
     * @param string $email
134
     * @return void
135
     */
136 4
    public function sendInviteEmail(string $email, string $hash): void
137
    {
138 4
        $userExists = Users::findFirst([
139 4
            'conditions' => 'email = ?0 and is_deleted = 0',
140 4
            'bind' => [$email]
141
        ]);
142
143 4
        $invitationUrl = $this->config->app->frontEndUrl . '/users/invites/' . $hash;
144
145 4
        if (is_object($userExists)) {
146
            $invitationUrl = $this->config->app->frontEndUrl . '/users/link/' . $hash;
147
        }
148
149 4
        if (!defined('API_TESTS')) {
150
            $subject = _('You have been invited!');
151
            $this->mail
152
            ->to($email)
153
            ->subject($subject)
154
            ->content($invitationUrl)
155
            ->sendNow();
156
        }
157 4
    }
158
159
    /**
160
     * Add invited user to our system
161
     * @return Response
162
     */
163 2
    public function processUserInvite(string $hash): Response
164
    {
165 2
        $request = $this->request->getPost();
166 2
        $password = ltrim(trim($request['password']));
167
168 2
        if (empty($request)) {
169
            $request = $this->request->getJsonRawBody(true);
170
        }
171
172
        //Ok let validate user password
173 2
        $validation = new Validation();
174 2
        $validation->add('password', new PresenceOf(['message' => _('The password is required.')]));
175
176 2
        $validation->add(
177 2
            'password',
178 2
            new StringLength([
179 2
                'min' => 8,
180 2
                'messageMinimum' => _('Password is too short. Minimum 8 characters.'),
181
            ])
182
        );
183
184
        //validate this form for password
185 2
        $messages = $validation->validate($request);
186 2
        if (count($messages)) {
187
            foreach ($messages as $message) {
188
                throw new ServerErrorHttpException((string)$message);
189
            }
190
        }
191
192
        //Lets find users_invite by hash on our database
193 2
        $usersInvite = $this->model::findFirst([
194 2
                'conditions' => 'invite_hash = ?0 and is_deleted = 0',
195 2
                'bind' => [$hash]
196
            ]);
197
198 2
        if (!is_object($usersInvite)) {
199
            throw new NotFoundHttpException('Users Invite not found');
200
        }
201
202
        //Check if user already exists
203 2
        $userExists = Users::findFirst([
204 2
            'conditions' => 'email = ?0 and is_deleted = 0',
205 2
            'bind' => [$usersInvite->email]
206
        ]);
207
208 2
        if (is_object($userExists)) {
209 1
            $newUser = new UsersAssociatedCompany;
210 1
            $newUser->users_id = (int)$userExists->id;
211 1
            $newUser->companies_id = (int)$userExists->default_company;
212 1
            $newUser->identify_id = $userExists->roles_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $userExists->roles_id can also be of type Phalcon\Mvc\Model\Resultset. However, the property $identify_id is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
213 1
            $newUser->user_active = 1;
214 1
            $newUser->user_role = Roles::getById((int)$userExists->roles_id)->name;
215
216 1
            if (!$newUser->save()) {
217 1
                throw new UnprocessableEntityHttpException((string) current($newUser->getMessages()));
218
            }
219
        } else {
220 2
            $newUser = new Users();
221 2
            $newUser->firstname = $request['firstname'];
222 2
            $newUser->lastname = $request['lastname'];
223 2
            $newUser->displayname = $request['displayname'];
224 2
            $newUser->password = $password;
225 2
            $newUser->email = $usersInvite->email;
226 2
            $newUser->user_active = 1;
227 2
            $newUser->roles_id = $usersInvite->role_id;
228 2
            $newUser->created_at = date('Y-m-d H:m:s');
229 2
            $newUser->default_company = $usersInvite->companies_id;
230 2
            $newUser->default_company_branch = $usersInvite->company->branch->getId();
231
232
            try {
233 2
                $this->db->begin();
234
235
                //signup
236 2
                $newUser->signup();
237
238 2
                $this->db->commit();
239
            } catch (Exception $e) {
240
                $this->db->rollback();
241
242
                throw new UnprocessableEntityHttpException($e->getMessage());
243
            }
244
        }
245
246
        //Lets login the new user
247 2
        $authInfo = $this->loginInvitedUser($usersInvite->email, $password);
248
249 2
        if (!defined('API_TESTS')) {
250
            $usersInvite->is_deleted = 1;
251
            $usersInvite->update();
252
253
            return $this->response($authInfo);
254
        }
255
256 2
        return $this->response($newUser);
257
    }
258
259
    /**
260
     * Login invited user
261
     * @param string
262
     * @return array
263
     */
264 2
    public function loginInvitedUser(string $email, string $password): array
265
    {
266 2
        $userIp = !defined('API_TESTS') ? $this->request->getClientAddress() : '127.0.0.1';
267
268 2
        $random = new \Phalcon\Security\Random();
269
270 2
        $userData = Users::login($email, $password, 1, 0, $userIp);
271
272 2
        $sessionId = $random->uuid();
273
274
        //save in user logs
275
        $payload = [
276 2
            'sessionId' => $sessionId,
277 2
            'email' => $userData->getEmail(),
278 2
            'iat' => time(),
279
        ];
280
281 2
        $token = $this->auth->make($payload);
282
283
        //start session
284 2
        $session = new Sessions();
285 2
        $session->start($userData, $sessionId, $token, $userIp, 1);
286
287
        return [
288 2
            'token' => $token,
289 2
            'time' => date('Y-m-d H:i:s'),
290 2
            'expires' => date('Y-m-d H:i:s', time() + $this->config->jwt->payload->exp),
291 2
            'id' => $userData->getId(),
292
        ];
293
    }
294
}
295