Test Failed
Push — master ( cd7674...f8cf92 )
by Alexey
04:46
created

Users::createSession()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 11
nc 1
nop 1
dl 0
loc 14
ccs 0
cts 11
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Users module
5
 *
6
 * @author Alexey Krupskiy <[email protected]>
7
 * @link http://inji.ru/
8
 * @copyright 2015 Alexey Krupskiy
9
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
10
 */
11
class Users extends Module {
12
13
    public $cookiePrefix = '';
14
15
    public function init() {
16
        if (!empty($this->config['cookieSplit'])) {
17
            $this->cookiePrefix = \App::$cur->type;
18
        }
19
        \Users\User::$cur = new Users\User(array('group_id' => 1, 'role_id' => 1));
20
        if (!App::$cur->db->connect) {
21
            return false;
22
        }
23
        if (isset($_GET['logout'])) {
24
            return $this->logOut();
25
        }
26
        if (isset($_GET['passre']) && filter_input(INPUT_GET, 'user_mail')) {
27
            $this->passre(trim(filter_input(INPUT_GET, 'user_mail')));
28
        }
29
        if (!empty($_GET['passrecont']) && filter_input(INPUT_GET, 'hash')) {
30
            $this->passrecont(filter_input(INPUT_GET, 'hash'));
31
        }
32
        if (isset($_POST['autorization']) && trim(filter_input(INPUT_POST, 'user_login')) && trim(filter_input(INPUT_POST, 'user_pass'))) {
33
            unset($_POST['autorization']);
34
            return $this->autorization(trim(filter_input(INPUT_POST, 'user_login')), trim(filter_input(INPUT_POST, 'user_pass')), strpos(filter_input(INPUT_POST, 'user_login'), '@') ? 'mail' : 'login', false);
35
        }
36
        if (filter_input(INPUT_COOKIE, $this->cookiePrefix . '_user_session_hash') && filter_input(INPUT_COOKIE, $this->cookiePrefix . '_user_id')) {
37
            return $this->cuntinueSession(filter_input(INPUT_COOKIE, $this->cookiePrefix . '_user_session_hash'), filter_input(INPUT_COOKIE, $this->cookiePrefix . '_user_id'));
38
        }
39
    }
40
41
    public function logOut($redirect = true) {
42
        if (!empty($_COOKIE[$this->cookiePrefix . "_user_session_hash"]) && !empty($_COOKIE[$this->cookiePrefix . "_user_id"])) {
43
            $session = Users\Session::get([
44
                        ['user_id', $_COOKIE[$this->cookiePrefix . "_user_id"]],
45
                        ['hash', $_COOKIE[$this->cookiePrefix . "_user_session_hash"]]
46
            ]);
47
            if ($session) {
48
                $session->delete();
49
            }
50
        }
51 View Code Duplication
        if (!headers_sent()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
            setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
53
            setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
54
        }
55
        if ($redirect) {
56 View Code Duplication
            if (!empty($this->config['logoutUrl'][$this->app->type])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
                Tools::redirect($this->config['logoutUrl'][$this->app->type]);
58
            }
59
            Tools::redirect('/', 'Вы вышли из своего профиля', 'success');
60
        }
61
    }
62
63
    public function cuntinueSession($hash, $userId) {
64
        $session = Users\Session::get([
65
                    ['user_id', $userId],
66
                    ['hash', $hash]
67
        ]);
68
        if ($session && $session->user && $session->user->blocked) {
69 View Code Duplication
            if (!headers_sent()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
70
                setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
71
                setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
72
            }
73
            Msg::add('Ваш аккаунт заблокирован', 'info');
74
            return;
75
        }
76
        if ($session && $session->user && !$session->user->blocked) {
77
            if (!empty($this->config['needActivation']) && $session->user->activation) {
78 View Code Duplication
                if (!headers_sent()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
79
                    setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
80
                    setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
81
                }
82
                Tools::redirect('/', 'Этот аккаунт ещё не активирован. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $session->user->id . '"><b>повторно выслать ссылку активации</b></a>');
83
            } elseif ($session->user->activation) {
84
                Msg::add('Этот аккаунт ещё не активирован, не все функции могут быть доступны. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $session->user->id . '"><b>повторно выслать ссылку активации</b></a>');
85
            }
86 View Code Duplication
            if (!$session->user->mail && !empty($this->config['noMailNotify'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
                Msg::add($this->config['noMailNotify']);
88
            }
89
            Users\User::$cur = $session->user;
90
            Users\User::$cur->date_last_active = 'CURRENT_TIMESTAMP';
91
            Users\User::$cur->save();
92 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
            if (!headers_sent()) {
94
                setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
95
                setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
96
            }
97
            Msg::add('Ваша сессия устарела или более недействительна, вам необходимо пройти <a href = "/users/login">авторизацию</a> заново', 'info');
98
        }
99
    }
100
101
    /**
102
     * @param string $user_mail
103
     */
104
    public function passre($user_mail) {
105
        $user = $this->get($user_mail, 'mail');
106
        if (!$user) {
107
            Msg::add('Пользователь ' . $user_mail . ' не найден, проверьте првильность ввода e-mail или зарегистрируйтесь', 'danger');
108
            return false;
109
        }
110
        $passre = Users\Passre::get([['user_id', $user->id], ['status', 1]]);
111
        if ($passre) {
112
            $passre->status = 2;
113
            $passre->save();
114
        }
115
        $hash = $user->id . '_' . Tools::randomString(50);
116
        $passre = new Users\Passre(['user_id' => $user->id, 'status' => 1, 'hash' => $hash]);
117
        $passre->save();
118
        Tools::sendMail('noreply@' . INJI_DOMAIN_NAME, $user_mail, 'Восстановление пароля на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME), 'Было запрошено восстановление пароля на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME) . '<br />для продолжения восстановления пароля перейдите по ссылке: <a href = "http://' . idn_to_utf8(INJI_DOMAIN_NAME) . '/?passrecont=1&hash=' . $hash . '">' . idn_to_utf8(INJI_DOMAIN_NAME) . '/?passrecont=1&hash=' . $hash . '</a>');
119
        Tools::redirect('/', 'На указанный почтовый ящик была выслана инструкция по восстановлению пароля', 'success');
120
    }
121
122
    public function passrecont($hash) {
123
        $passre = Users\Passre::get([['hash', $hash]]);
124
        if ($passre) {
125
            if ($passre->status != 1) {
126
                Tools::redirect('/', 'Этот код восстановление более недействителен', 'danger');
127
            }
128
            $passre->status = 3;
129
            $passre->save();
130
            $pass = Tools::randomString(10);
131
            $user = Users\User::get($passre->user_id);
132
            $user->pass = $this->hashpass($pass);
133
            $user->save();
134
            $this->autorization($user->id, $pass, 'id', true, true);
135
            Tools::sendMail('noreply@' . INJI_DOMAIN_NAME, $user->mail, 'Новый пароль на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME), 'Было запрошено восстановление пароля на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME) . '<br />Ваш новый пароль: ' . $pass);
136
            Tools::redirect('/', 'Вы успешно сбросили пароль и были авторизованы на сайте. На ваш почтовый ящик был выслан новый пароль', 'success');
137
        }
138
    }
139
140
    public function autorization($login, $pass, $ltype = 'login', $noMsg = true, $skipErrorCheck = false) {
141
        $user = $this->get($login, $ltype);
142
        if ($user && !$skipErrorCheck) {
143
            $lastSuccessLogin = \Users\User\LoginHistory::lastSuccessLogin($user->id);
144
            $where = [['user_id', $user->id]];
145
            if ($lastSuccessLogin) {
146
                $where[] = ['date_create', $lastSuccessLogin->date_create, '>'];
147
            }
148
            $loginHistoryErrorCount = \Users\User\LoginHistory::getCount(['where' => $where]);
149
            if ($loginHistoryErrorCount > 5) {
150
                Msg::add('Было совершено более 5ти попыток подбора пароля к вашему аккаунту, для вашей безопасности мы были вынуждены заблокировать к нему доступ.<br />
151
Для разблокировки аккаунта, воспользуйтесь <a href = "?passre=1&user_mail=' . $user->mail . '">Сбросом пароля</a>', 'danger');
152
                return false;
153
            }
154
        }
155
        if ($user && $this->verifypass($pass, $user->pass) && !$user->blocked) {
156
            $loginHistory = new \Users\User\LoginHistory([
157
                'user_id' => $user->id,
158
                'ip' => $_SERVER['REMOTE_ADDR'],
159
                'success' => true
160
            ]);
161
            $loginHistory->save();
162
            if (!empty($this->config['needActivation']) && $user->activation) {
163
                Tools::redirect('/', 'Этот аккаунт ещё не активирован. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $user->id . '"><b>повторно выслать ссылку активации</b></a>');
164
            } elseif ($user->activation) {
165
                Msg::add('Этот аккаунт ещё не активирован, не все функции могут быть доступны. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $user->id . '"><b>повторно выслать ссылку активации</b></a>');
166
            }
167 View Code Duplication
            if (!$user->mail && !empty($this->config['noMailNotify'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
                Msg::add($this->config['noMailNotify']);
169
            }
170
            $this->newSession($user);
171
172
            Users\User::$cur = $user;
173
            Users\User::$cur->date_last_active = 'CURRENT_TIMESTAMP';
174
            Users\User::$cur->save();
175 View Code Duplication
            if (!$noMsg && !empty($this->config['loginUrl'][$this->app->type])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
                Tools::redirect($this->config['loginUrl'][$this->app->type]);
177
            }
178
            return true;
179
        }
180
        if (!$noMsg) {
181
            if ($user && $user->blocked) {
182
                Msg::add('Вы заблокированы', 'danger');
183
            } elseif ($user) {
184
                $loginHistory = new \Users\User\LoginHistory([
185
                    'user_id' => $user->id,
186
                    'ip' => $_SERVER['REMOTE_ADDR'],
187
                    'success' => false
188
                ]);
189
                $loginHistory->save();
190
                Msg::add('Вы ошиблись при наборе пароля или логина, попробуйте ещё раз или воспользуйтесь <a href = "?passre=1&user_mail=' . $user->mail . '">Восстановлением пароля</a>', 'danger');
191
            } else {
192
                Msg::add('Данный почтовый ящик не зарегистрирован в системе', 'danger');
193
            }
194
        }
195
196
        return false;
197
    }
198
199
    public function newSession($user) {
200
        $session = $this->createSession($user);
201
        if (!headers_sent()) {
202
            setcookie($this->cookiePrefix . "_user_session_hash", $session->hash, time() + 360000, "/");
203
            setcookie($this->cookiePrefix . "_user_id", $session->user_id, time() + 360000, "/");
204
        } else {
205
            Msg::add('Не удалось провести авторизацию. Попробуйте позже', 'info');
206
        }
207
    }
208
209
    public function createSession($user) {
210
        do {
211
            $hash = Tools::randomString(255);
212
        } while (Users\Session::get($hash, 'hash'));
213
214
        $session = new Users\Session([
215
            'user_id' => $user->id,
216
            'agent' => $_SERVER['HTTP_USER_AGENT'],
217
            'ip' => $_SERVER['REMOTE_ADDR'],
218
            'hash' => $hash
219
        ]);
220
        $session->save();
221
        return $session;
222
    }
223
224
    /**
225
     * Return user
226
     *
227
     * @param integer|string $idn
228
     * @param type $ltype
229
     * @return boolean|\User\User
230
     */
231
    public function get($idn, $ltype = 'id') {
232
        if (!$idn)
233
            return false;
234
235
        if (is_numeric($idn) && $ltype != 'login')
236
            $user = Users\User::get($idn, 'id');
237
        elseif ($ltype == 'login')
238
            $user = Users\User::get($idn, 'login');
239
        else
240
            $user = Users\User::get($idn, 'mail');
241
        if (!$user)
242
            return [];
243
244
        return $user;
245
    }
246
247
    public function registration($data, $autorization = false) {
248
249
        if (empty($data['user_mail'])) {
250
            Msg::add('Вы не ввели E-mail', 'danger');
251
            return false;
252
        }
253
        $data['user_mail'] = trim($data['user_mail']);
254
        if (!filter_var($data['user_mail'], FILTER_VALIDATE_EMAIL)) {
255
            Msg::add('Вы ввели не корректный E-mail', 'danger');
256
            return false;
257
        }
258
259
        $user = $this->get($data['user_mail'], 'mail');
260
        if ($user) {
261
            Msg::add('Введенный вами E-mail зарегистрирован в нашей системе, войдите или введите другой E-mail', 'danger');
262
            return false;
263
        }
264
        if (empty($data['user_login'])) {
265
            $data['user_login'] = $data['user_mail'];
266
        }
267
        $data['user_login'] = trim($data['user_login']);
268
        $user = $this->get($data['user_login'], 'login');
269
        if ($user) {
270
            Msg::add('Введенный вами логин зарегистрирован в нашей системе, войдите или введите другой логин', 'danger');
271
            return false;
272
        }
273
        if (empty($data['first_name'])) {
274
            $data['first_name'] = '';
275
        }
276
        if (empty($data['last_name'])) {
277
            $data['last_name'] = '';
278
        }
279
        if (!empty($data['user_name'])) {
280
            $data['first_name'] = $data['user_name'];
281
        }
282
        if (empty($data['user_city'])) {
283
            $data['user_city'] = '';
284
        }
285
        if (empty($data['user_birthday'])) {
286
            $data['user_birthday'] = '';
287
        }
288
        if (empty($data['user_phone'])) {
289
            $data['user_phone'] = '';
290
        }
291
        $invite_code = (!empty($data['invite_code']) ? $data['invite_code'] : (!empty($_POST['invite_code']) ? $_POST['invite_code'] : ((!empty($_COOKIE['invite_code']) ? $_COOKIE['invite_code'] : ((!empty($_GET['invite_code']) ? $_GET['invite_code'] : ''))))));
292
        if (!empty($invite_code)) {
293
            $invite = Users\User\Invite::get($invite_code, 'code');
294
            if (!$invite) {
295
                Msg::add('Такой код приглашения не найден', 'danger');
296
                return false;
297
            }
298
            if ($invite->limit && !($invite->limit - $invite->count)) {
299
                Msg::add('Лимит приглашений для данного кода исчерпан', 'danger');
300
                return false;
301
            }
302
            $data['parent_id'] = $invite->user_id;
303
            $inviter = $data['parent_id'];
304
            $invite->count++;
305
            $invite->save();
306
        }
307
        if (empty($data['parent_id']) && !empty($this->config['defaultPartner'])) {
308
            $data['parent_id'] = $this->config['defaultPartner'];
309
        }
310
        if (!empty($data['user_pass'])) {
311
            if (empty($data['user_pass'][0])) {
312
                Msg::add('Введите пароль', 'danger');
313
                return false;
314
            }
315
            if (empty($data['user_pass'][1])) {
316
                Msg::add('Повторите ввод пароля', 'danger');
317
                return false;
318
            }
319
            if ($data['user_pass'][0] != $data['user_pass'][1]) {
320
                Msg::add('Введенные пароли несовпадают', 'danger');
321
                return false;
322
            }
323
            $pass = $data['user_pass'][0];
324
        } else {
325
            $pass = Tools::randomString(10);
326
        }
327
328
        $user = new Users\User([
329
            'pass' => $this->hashpass($pass),
330
            'mail' => $data['user_mail'],
331
            'login' => htmlspecialchars($data['user_login']),
332
            'role_id' => 2,
333
            'group_id' => 2,
334
            'parent_id' => !empty($data['parent_id']) ? $data['parent_id'] : 0
335
        ]);
336
        if (!empty($this->config['needActivation'])) {
337
            $user->activation = Tools::randomString();
338
        }
339
        $user->save();
340
        if (!$user->id) {
341
            Msg::add('Не удалось зарегистрировать', 'danger');
342
            return false;
343
        }
344
        $info = new \Users\User\Info([
345
            'user_id' => $user->id,
346
            'first_name' => htmlspecialchars($data['first_name']),
347
            'last_name' => htmlspecialchars($data['last_name']),
348
            'city' => htmlspecialchars($data['user_city']),
349
            'bday' => htmlspecialchars($data['user_birthday']),
350
            'phone' => htmlspecialchars($data['user_phone']),
351
            'photo_file_id' => !empty($_FILES['user_photo']['tmp_name']) ? $this->files->upload($_FILES['user_photo']) : 0
352
        ]);
353
        $info->save();
354
        if (isset($inviter)) {
355
            $this->AddUserActivity($inviter, 2, "У вас зарегистрировался новый партнер, {$info->first_name} {$info->last_name} (id: {$user->id}, email: {$user->mail})");
356
        }
357
        if ($autorization) {
358
            $this->autorization($data['user_mail'], $pass, 'mail');
359
        }
360
        if (!empty($this->config['needActivation'])) {
361
            $from = 'noreply@' . INJI_DOMAIN_NAME;
362
            $to = $data['user_mail'];
363
            $subject = 'Регистрация на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME);
364
            $text = 'Вы были зарегистрированы на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME) . '<br />для входа используйте ваш почтовый ящик в качестве логина и пароль: ' . $pass;
365
            $text .= '<br />';
366
            $text .= '<br />';
367
            $text .= 'Для активации вашего аккаунта перейдите по ссылке <a href = "http://' . INJI_DOMAIN_NAME . '/users/activation/' . $user->id . '/' . $user->activation . '">http://' . idn_to_utf8(INJI_DOMAIN_NAME) . '/users/activation/' . $user->id . '/' . $user->activation . '</a>';
368
            Tools::sendMail($from, $to, $subject, $text);
369
            Msg::add('Вы были зарегистрированы. На указанный почтовый ящик был выслан ваш пароль и ссылка для активации', 'success');
370
        } else {
371
            $from = 'noreply@' . INJI_DOMAIN_NAME;
372
            $to = $data['user_mail'];
373
            $subject = 'Регистрация на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME);
374
            $text = 'Вы были зарегистрированы на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME) . '<br />для входа используйте ваш почтовый ящик в качестве логина и пароль: ' . $pass;
375
            Tools::sendMail($from, $to, $subject, $text);
376
            Msg::add('Вы были зарегистрированы. На указанный почтовый ящик был выслан ваш пароль', 'success');
377
        }
378
        return $user->id;
379
    }
380
381
    public function hashpass($pass) {
382
        return password_hash($pass, PASSWORD_DEFAULT);
383
    }
384
385
    public function verifypass($pass, $hash) {
386
        return password_verify($pass, $hash);
387
    }
388
389
    public function getUserPartners($user, $levelsCount = 0) {
390
        $return = [
391
            'users' => [],
392
            'levels' => [],
393
            'count' => 0,
394
            'lastLevel' => 0
395
        ];
396
        $userIds = $user->user_id;
397
        for ($i = 1; $i <= $levelsCount || !$levelsCount; $i++) {
398
            if (!$userIds && $levelsCount) {
399
                $return['levels'][$i] = [];
400
                continue;
401
            } elseif (!$userIds && !$levelsCount) {
402
                break;
403
            }
404
            $usersLevel = \Users\User::getList(['where' => [['parent_id', $userIds, 'IN']]]);
405
            $return['users'] += $usersLevel;
406
            $return['levels'][$i] = array_keys($usersLevel);
407
            $userIds = implode(',', $return['levels'][$i]);
408
            $return['lastLevel'] = $i;
409
        }
410
        $return['count'] = count($return['users']);
411
        return $return;
412
    }
413
414
    /**
415
     * @param integer $cat_id
416
     */
417
    public function addUserActivity($user_id, $cat_id, $text = '') {
418
        $ua = new Users\Activity([
419
            'user_id' => $user_id,
420
            'category_id' => $cat_id,
421
            'text' => $text,
422
        ]);
423
        $ua->save();
424
    }
425
}