Passed
Push — v5 ( e348bf...8a8f01 )
by Alexey
06:35
created

Users::createSession()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 1
nop 1
dl 0
loc 13
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 (!empty($_GET['invite_code']) && is_string($_GET['invite_code'])) {
21
            setcookie('invite_code', $_GET['invite_code'], time() + 360000, "/");
22
        }
23
        if (!App::$cur->db->connect) {
24
            return false;
25
        }
26
        if (isset($_GET['logout'])) {
27
            return $this->logOut();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->logOut() targeting Users::logOut() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
28
        }
29
        if (isset($_GET['passre']) && filter_input(INPUT_GET, 'user_mail')) {
30
            $this->passre(trim(filter_input(INPUT_GET, 'user_mail')));
31
        }
32
        if (!empty($_GET['passrecont']) && filter_input(INPUT_GET, 'hash')) {
33
            $this->passrecont(filter_input(INPUT_GET, 'hash'));
34
        }
35
        if (isset($_POST['autorization']) && trim(filter_input(INPUT_POST, 'user_login')) && trim(filter_input(INPUT_POST, 'user_pass'))) {
36
            unset($_POST['autorization']);
37
            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, false, trim(filter_input(INPUT_POST, 'ref')));
38
        }
39
        if (!empty($_COOKIE[$this->cookiePrefix . '_user_session_hash']) && is_string($_COOKIE[$this->cookiePrefix . '_user_session_hash']) && !empty($_COOKIE[$this->cookiePrefix . '_user_id']) && is_string($_COOKIE[$this->cookiePrefix . '_user_id'])) {
40
            return $this->cuntinueSession($_COOKIE[$this->cookiePrefix . '_user_session_hash'], $_COOKIE[$this->cookiePrefix . '_user_id']);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->cuntinueSession($...iePrefix . '_user_id']) targeting Users::cuntinueSession() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
41
        }
42
    }
43
44
    public function logOut($redirect = true) {
45
        if (!empty($_COOKIE[$this->cookiePrefix . "_user_session_hash"]) && !empty($_COOKIE[$this->cookiePrefix . "_user_id"])) {
46
            $session = Users\Session::get([
47
                ['user_id', $_COOKIE[$this->cookiePrefix . "_user_id"]],
48
                ['hash', $_COOKIE[$this->cookiePrefix . "_user_session_hash"]]
49
            ]);
50
            if ($session) {
51
                $session->delete();
52
            }
53
        }
54
        if (!headers_sent()) {
55
            setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
56
            setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
57
        }
58
        if ($redirect) {
59
            if (!empty($this->config['logoutUrl'][$this->app->type])) {
60
                Tools::redirect($this->config['logoutUrl'][$this->app->type]);
0 ignored issues
show
Bug introduced by
The type Tools 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...
61
            }
62
            Tools::redirect('/', \I18n\Text::module('Users', 'Вы вышли из своего профиля'), 'success');
63
        }
64
    }
65
66
    public function cuntinueSession($hash, $userId) {
67
        $session = Users\Session::get([
68
            ['user_id', $userId],
69
            ['hash', $hash]
70
        ]);
71
        if (!$session) {
72
            if (!headers_sent()) {
73
                setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
74
                setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
75
            }
76
            Tools::redirect('/', \I18n\Text::module('Users', 'Произошла непредвиденная ошибка при авторизации сессии'));
77
        }
78
        if ($session->user->id != $userId) {
79
            Tools::redirect('/', \I18n\Text::module('Users', 'Произошла непредвиденная ошибка при авторизации сессии'));
80
        }
81
        if ($session && $session->user && $session->user->blocked) {
82
            if (!headers_sent()) {
83
                setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
84
                setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
85
            }
86
            Msg::add(\I18n\Text::module('Users', 'Ваш аккаунт заблокирован'), 'info');
0 ignored issues
show
Bug introduced by
The type Msg 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...
87
            return;
88
        }
89
        if ($session && $session->user && !$session->user->blocked) {
90
            if (!empty($this->config['needActivation']) && $session->user->activation) {
91
                if (!headers_sent()) {
92
                    setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
93
                    setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
94
                }
95
                Tools::redirect('/', 'Этот аккаунт ещё не активирован. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $session->user->id . '"><b>повторно выслать ссылку активации</b></a>');
96
            } elseif ($session->user->activation) {
97
                Msg::add('Этот аккаунт ещё не активирован, не все функции могут быть доступны. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $session->user->id . '"><b>повторно выслать ссылку активации</b></a>');
98
            }
99
            if (!$session->user->mail && !empty($this->config['noMailNotify'])) {
100
                Msg::add($this->config['noMailNotify']);
101
            }
102
            Users\User::$cur = $session->user;
103
            Users\User::$cur->date_last_active = 'CURRENT_TIMESTAMP';
104
            Users\User::$cur->save();
105
        } else {
106
            if (!headers_sent()) {
107
                setcookie($this->cookiePrefix . "_user_session_hash", '', 0, "/");
108
                setcookie($this->cookiePrefix . "_user_id", '', 0, "/");
109
            }
110
            Msg::add(\I18n\Text::module('Users', 'needrelogin'), 'info');
111
        }
112
    }
113
114
    /**
115
     * @param string $user_mail
116
     */
117
    public function passre($user_mail) {
118
        $user = $this->get($user_mail, 'mail');
119
        if (!$user) {
120
            Msg::add(\I18n\Text::module('Users', 'mailnotfound', ['user_mail' => $user_mail]), 'danger');
121
            return false;
122
        }
123
        $passre = Users\Passre::get([['user_id', $user->id], ['status', 1]]);
0 ignored issues
show
Bug introduced by
The property id does not exist on string.
Loading history...
124
        if ($passre) {
125
            $passre->status = 2;
126
            $passre->save();
127
        }
128
        $hash = $user->id . '_' . Tools::randomString(50);
129
        $passre = new Users\Passre(['user_id' => $user->id, 'status' => 1, 'hash' => $hash]);
130
        $passre->save();
131
        $domainRaw = App::$cur->getDomain();
132
        $domain = App::$cur->getDomain(true);
133
        $title = \I18n\Text::module('Users', 'Восстановление пароля на сайте ${domain}', ['domain' => $domain]);
134
        $text = \I18n\Text::module('Users', 'repassmailtext', ['domain' => $domain, 'hash' => $hash]);
135
        Tools::sendMail('noreply@' . $domainRaw, $user_mail, $title, $text);
136
        Tools::redirect('/', \I18n\Text::module('Users', 'На указанный почтовый ящик была выслана инструкция по восстановлению пароля'), 'success');
137
    }
138
139
    public function passrecont($hash) {
140
        $passre = Users\Passre::get([['hash', $hash]]);
141
        if ($passre) {
142
            if ($passre->status != 1) {
143
                Tools::redirect('/', 'Этот код восстановление более недействителен', 'danger');
144
            }
145
            $passre->status = 3;
146
            $passre->save();
147
            $pass = Tools::randomString(10);
148
            $user = Users\User::get($passre->user_id);
149
            $user->pass = $this->hashpass($pass);
150
            $user->save();
151
            $this->autorization($user->id, $pass, 'id', true, true);
152
            $domainRaw = App::$cur->getDomain();
153
            $domain = App::$cur->getDomain(true);
154
            $title = \I18n\Text::module('Users', 'Новый пароль на сайте ${domain}', ['domain' => $domain]);
155
            $text = \I18n\Text::module('Users', 'newpassmail', ['domain' => $domain, 'pass' => $pass]);
156
            Tools::sendMail('noreply@' . $domainRaw, $user->mail, $title, $text);
157
            Tools::redirect('/', \I18n\Text::module('Users', 'Вы успешно сбросили пароль и были авторизованы на сайте. На ваш почтовый ящик был выслан новый пароль'), 'success');
158
        }
159
    }
160
161
    public function autorization($login, $pass, $ltype = 'login', $noMsg = true, $skipErrorCheck = false, $redirect = '') {
162
        $user = $this->get($login, $ltype);
163
        if ($user && !$skipErrorCheck) {
164
            $lastSuccessLogin = \Users\User\LoginHistory::lastSuccessLogin($user->id);
165
            $where = [['user_id', $user->id]];
166
            if ($lastSuccessLogin) {
167
                $where[] = ['date_create', $lastSuccessLogin->date_create, '>'];
168
            }
169
            $loginHistoryErrorCount = \Users\User\LoginHistory::getCount(['where' => $where]);
170
            if ($loginHistoryErrorCount > 5) {
171
                Msg::add(\I18n\Text::module('Users', 'logintrylimit', ['user_mail' => $user->mail]), 'danger');
172
                return false;
173
            }
174
        }
175
        if ($user && $this->verifypass($pass, $user->pass) && !$user->blocked) {
176
            $loginHistory = new \Users\User\LoginHistory([
177
                'user_id' => $user->id,
178
                'ip' => $_SERVER['REMOTE_ADDR'],
179
                'success' => true
180
            ]);
181
            $loginHistory->save();
182
            if (!empty($this->config['needActivation']) && $user->activation) {
183
                Tools::redirect('/', 'Этот аккаунт ещё не активирован. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $user->id . '"><b>повторно выслать ссылку активации</b></a>');
184
            } elseif ($user->activation) {
185
                Msg::add('Этот аккаунт ещё не активирован, не все функции могут быть доступны. <br />Если вы не получали письмо с ссылкой для активации, нажмите на - <a href = "/users/resendActivation/' . $user->id . '"><b>повторно выслать ссылку активации</b></a>');
186
            }
187
            if (!$user->mail && !empty($this->config['noMailNotify'])) {
188
                Msg::add($this->config['noMailNotify']);
189
            }
190
            $this->newSession($user);
191
192
            Users\User::$cur = $user;
193
            Users\User::$cur->date_last_active = 'CURRENT_TIMESTAMP';
194
            Users\User::$cur->save();
195
            if (!$noMsg) {
196
                if (!empty($this->config['loginUrl'][$this->app->type]) && !$redirect) {
197
                    $redirect = $this->config['loginUrl'][$this->app->type];
198
                }
199
                Tools::redirect($redirect);
200
            }
201
202
            return true;
203
        }
204
        if (!$noMsg) {
205
            if ($user && $user->blocked) {
206
                Msg::add('Вы заблокированы', 'danger');
207
            } elseif ($user) {
208
                $loginHistory = new \Users\User\LoginHistory([
209
                    'user_id' => $user->id,
210
                    'ip' => $_SERVER['REMOTE_ADDR'],
211
                    'success' => false
212
                ]);
213
                $loginHistory->save();
214
                Msg::add(\I18n\Text::module('Users', 'loginfail', ['user_mail' => $user->mail]), 'danger');
215
            } else {
216
                Msg::add(\I18n\Text::module('Users', 'Данный почтовый ящик не зарегистрирован в системе'), 'danger');
217
            }
218
        }
219
220
        return false;
221
    }
222
223
    public function newSession($user) {
224
        $session = $this->createSession($user);
225
        if (!headers_sent()) {
226
            setcookie($this->cookiePrefix . "_user_session_hash", $session->hash, time() + 360000, "/");
227
            setcookie($this->cookiePrefix . "_user_id", $user->id, time() + 360000, "/");
228
        } else {
229
            Msg::add('Не удалось провести авторизацию. Попробуйте позже', 'info');
230
        }
231
    }
232
233
    public function createSession($user) {
234
        do {
235
            $hash = Tools::randomString(255);
236
        } while (Users\Session::get($hash, 'hash'));
237
238
        $session = new Users\Session([
239
            'user_id' => $user->id,
240
            'agent' => $_SERVER['HTTP_USER_AGENT'],
241
            'ip' => $_SERVER['REMOTE_ADDR'],
242
            'hash' => $hash
243
        ]);
244
        $session->save();
245
        return $session;
246
    }
247
248
    /**
249
     * Return user
250
     *
251
     * @param integer|string $idn
252
     * @param string $ltype
253
     * @return boolean|\Users\User
254
     */
255
    public function get($idn, $ltype = 'id') {
256
        if (!$idn)
257
            return false;
258
259
        if (is_numeric($idn) && $ltype != 'login')
260
            $user = Users\User::get($idn, 'id');
261
        elseif ($ltype == 'login')
262
            $user = Users\User::get($idn, 'login');
263
        else
264
            $user = Users\User::get($idn, 'mail');
265
        if (!$user)
266
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type boolean|Users\User.
Loading history...
267
268
        return $user;
269
    }
270
271
    private function msgOrErr($err, $msg) {
272
        if ($msg) {
273
            Msg::add($err, 'danger');
274
            return false;
275
        }
276
        return ['success' => false, 'error' => $err];
277
278
    }
279
280
    public function registration($data, $autorization = false, $msg = true) {
281
282
        if (empty($data['user_mail'])) {
283
            return $this->msgOrErr('Вы не ввели E-mail', $msg);
284
        }
285
        if (!empty($this->config['csrf']) && !App::$cur->server->checkCsrfForm($data)) {
286
            return $this->msgOrErr('Ошибка безопасности, повторите запрос еще раз', $msg);
287
        }
288
        $data['user_mail'] = trim($data['user_mail']);
289
        if (!filter_var($data['user_mail'], FILTER_VALIDATE_EMAIL)) {
290
            return $this->msgOrErr(\I18n\Text::module('Users', 'Вы ввели не корректный E-mail'), $msg);
291
292
        }
293
294
        $user = $this->get($data['user_mail'], 'mail');
295
        if ($user) {
296
            return $this->msgOrErr(\I18n\Text::module('Users', 'Введенный вами E-mail зарегистрирован в нашей системе, войдите или введите другой E-mail'), $msg);
297
        }
298
        if (empty($data['user_login'])) {
299
            $data['user_login'] = $data['user_mail'];
300
        }
301
        $data['user_login'] = trim($data['user_login']);
302
        $user = $this->get($data['user_login'], 'login');
303
        if ($user) {
304
            return $this->msgOrErr('Введенный вами логин зарегистрирован в нашей системе, войдите или введите другой логин', $msg);
305
        }
306
        if (empty($data['first_name'])) {
307
            $data['first_name'] = '';
308
        }
309
        if (empty($data['last_name'])) {
310
            $data['last_name'] = '';
311
        }
312
        if (!empty($data['user_name'])) {
313
            $data['first_name'] = $data['user_name'];
314
        }
315
        if (empty($data['user_city'])) {
316
            $data['user_city'] = '';
317
        }
318
        if (empty($data['user_birthday'])) {
319
            $data['user_birthday'] = '';
320
        }
321
        if (empty($data['user_phone'])) {
322
            $data['user_phone'] = '';
323
        }
324
        $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'] : ''))))));
325
        if (!empty($invite_code)) {
326
            $invite = Users\User\Invite::get($invite_code, 'code');
327
            if (!$invite) {
328
                return $this->msgOrErr('Такой код приглашения не найден', $msg);
329
            }
330
            if ($invite->limit && !($invite->limit - $invite->count)) {
331
                return $this->msgOrErr('Лимит приглашений для данного кода исчерпан', $msg);
332
            }
333
            $data['parent_id'] = $invite->user_id;
334
            $inviter = $data['parent_id'];
335
            $invite->count++;
336
            $invite->save();
337
        }
338
        if (empty($data['parent_id']) && !empty($this->config['defaultPartner'])) {
339
            $data['parent_id'] = $this->config['defaultPartner'];
340
        }
341
        if (!empty($this->config['passwordManualSetup']) && !empty($data['user_pass'])) {
342
            if (empty($data['user_pass'][0])) {
343
                return $this->msgOrErr('Введите пароль', $msg);
344
            }
345
            if (empty($data['user_pass'][1])) {
346
                return $this->msgOrErr('Повторите ввод пароля', $msg);
347
            }
348
            if ($data['user_pass'][0] != $data['user_pass'][1]) {
349
                return $this->msgOrErr('Введенные пароли несовпадают', $msg);
350
            }
351
            $pass = $data['user_pass'][0];
352
        } else {
353
            $pass = Tools::randomString(10);
354
        }
355
356
        $user = new Users\User([
357
            'pass' => $this->hashpass($pass),
358
            'mail' => $data['user_mail'],
359
            'login' => htmlspecialchars($data['user_login']),
360
            'role_id' => 2,
361
            'group_id' => 2,
362
            'parent_id' => !empty($data['parent_id']) ? $data['parent_id'] : 0
363
        ]);
364
        if (!empty($this->config['needActivation'])) {
365
            $user->activation = Tools::randomString();
366
        }
367
        $user->save();
368
        if (!$user->id) {
369
            return $this->msgOrErr('Не удалось зарегистрировать', $msg);
370
        }
371
        $info = new \Users\User\Info([
372
            'user_id' => $user->id,
373
            'first_name' => htmlspecialchars($data['first_name']),
374
            'last_name' => htmlspecialchars($data['last_name']),
375
            'city' => htmlspecialchars($data['user_city']),
376
            'bday' => htmlspecialchars($data['user_birthday']),
377
            'phone' => htmlspecialchars($data['user_phone']),
378
            'photo_file_id' => !empty($_FILES['user_photo']['tmp_name']) ? $this->files->upload($_FILES['user_photo']) : 0
379
        ]);
380
        $info->save();
381
        if (isset($inviter)) {
382
            $this->AddUserActivity($inviter, 2, "У вас зарегистрировался новый партнер, {$info->first_name} {$info->last_name} (id: {$user->id}, email: {$user->mail})");
383
        }
384
        if ($autorization) {
385
            $this->autorization($data['user_mail'], $pass, 'mail');
386
        }
387
        if (!empty($this->config['needActivation'])) {
388
            $from = 'noreply@' . INJI_DOMAIN_NAME;
389
            $to = $data['user_mail'];
390
            $subject = 'Регистрация на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME);
391
            $text = 'Вы были зарегистрированы на сайте ' . idn_to_utf8(INJI_DOMAIN_NAME) . '<br />для входа используйте ваш почтовый ящик в качестве логина и пароль: ' . $pass;
392
            $text .= '<br />';
393
            $text .= '<br />';
394
            $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>';
395
            Tools::sendMail($from, $to, $subject, $text);
396
            if ($msg) {
397
                Msg::add('Вы были зарегистрированы. На указанный почтовый ящик был выслан ваш пароль и ссылка для активации', 'success');
398
            }
399
        } else {
400
            $from = 'noreply@' . INJI_DOMAIN_NAME;
401
            $to = $data['user_mail'];
402
            $subject = \I18n\Text::module('Users', 'Регистрация на сайте ${sitename}', ['sitename' => idn_to_utf8(INJI_DOMAIN_NAME)]);
403
            $text = \I18n\Text::module('Users', 'sucregmsg', [
404
                'sitename' => idn_to_utf8(INJI_DOMAIN_NAME),
405
                'pass' => $pass
406
            ]);
407
            Tools::sendMail($from, $to, $subject, $text);
408
            if ($msg) {
409
                Msg::add(\I18n\Text::module('Users', 'Вы были зарегистрированы. На указанный почтовый ящик был выслан ваш пароль'), 'success');
410
            }
411
        }
412
        return $user->id;
413
    }
414
415
    public function hashpass($pass) {
416
        return password_hash($pass, PASSWORD_DEFAULT);
417
    }
418
419
    public function verifypass($pass, $hash) {
420
        return password_verify($pass, $hash);
421
    }
422
423
    public function getUserPartners($user, $levelsCount = 0) {
424
        $return = [
425
            'users' => [],
426
            'levels' => [],
427
            'count' => 0,
428
            'lastLevel' => 0
429
        ];
430
        $userIds = $user->user_id;
431
        for ($i = 1; $i <= $levelsCount || !$levelsCount; $i++) {
432
            if (!$userIds && $levelsCount) {
433
                $return['levels'][$i] = [];
434
                continue;
435
            } elseif (!$userIds && !$levelsCount) {
436
                break;
437
            }
438
            $usersLevel = \Users\User::getList(['where' => [['parent_id', $userIds, 'IN']]]);
439
            $return['users'] += $usersLevel;
440
            $return['levels'][$i] = array_keys($usersLevel);
441
            $userIds = implode(',', $return['levels'][$i]);
442
            $return['lastLevel'] = $i;
443
        }
444
        $return['count'] = count($return['users']);
445
        return $return;
446
    }
447
448
    /**
449
     * @param integer $cat_id
450
     */
451
    public function addUserActivity($user_id, $cat_id, $text = '') {
452
        $ua = new Users\Activity([
453
            'user_id' => $user_id,
454
            'category_id' => $cat_id,
455
            'text' => $text,
456
        ]);
457
        $ua->save();
458
    }
459
}