Test Failed
Push — master ( cf7a15...101480 )
by Alexey
05:05
created

Users::msgOrErr()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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