Completed
Push — work-fleets ( 47cb39...a1ac53 )
by SuperNova.WS
05:19
created

auth_local::flog()   C

Complexity

Conditions 11
Paths 193

Size

Total Lines 19
Code Lines 13

Duplication

Lines 19
Ratio 100 %
Metric Value
dl 19
loc 19
rs 6.3808
cc 11
eloc 13
nc 193
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Class auth_local
5
 */
6
// Расширяет sn_module, потому что его потомки так же являются модулями
7
class auth_local extends auth_abstract {
8
  public $manifest = array(
9
    'package' => 'auth',
10
    'name' => 'local',
11
    'version' => '0a0',
12
    'copyright' => 'Project "SuperNova.WS" #41a6.95# copyright © 2009-2015 Gorlum',
13
14
    'root_relative' => '',
15
16
    'load_order' => 2,
17
18
    'installed' => true,
19
    'active' => true,
20
21
    'config_path' => SN_ROOT_PHYSICAL,
22
  );
23
24
  public $provider_id = ACCOUNT_PROVIDER_LOCAL;
25
26
  /**
27
   * Флаг входа в игру
28
   *
29
   * @var bool
30
   */
31
  protected $is_login = false;
32
  /**
33
   * Флаг регистрации
34
   *
35
   * @var bool
36
   */
37
  protected $is_register = false;
38
  /**
39
   * Флаг запроса кода на сброс пароля
40
   *
41
   * @var bool
42
   */
43
  protected $is_password_reset = false;
44
  /**
45
   * Флаг запроса на сброс пароля по коду
46
   *
47
   * @var bool
48
   */
49
  protected $is_password_reset_confirm = false;
50
  /**
51
   * Нужно ли запоминать креденшиался при выходе из браузера
52
   *
53
   * @var bool
54
   */
55
  protected $remember_me = 1;
56
57
  /**
58
   * @var Confirmation
59
   */
60
  protected $confirmation = null;
61
62
63
  protected $features = array(
64
    AUTH_FEATURE_EMAIL_CHANGE => AUTH_FEATURE_EMAIL_CHANGE,
65
    AUTH_FEATURE_PASSWORD_RESET => AUTH_FEATURE_PASSWORD_RESET,
66
    AUTH_FEATURE_PASSWORD_CHANGE => AUTH_FEATURE_PASSWORD_CHANGE,
67
    AUTH_FEATURE_HAS_PASSWORD => AUTH_FEATURE_HAS_PASSWORD,
68
  );
69
70
  /**
71
   * @var string $input_login_unsafe
72
   */
73
  protected $input_login_unsafe = '';
74
  protected $input_login_password_raw = '';
75
  protected $input_login_password_raw_repeat = '';
76
  protected $input_email_unsafe = '';
77
  protected $input_language_unsafe = '';
78
  protected $input_language_safe = '';
79
80
  protected $domain = null;
81
  protected $sn_root_path = null;
82
  protected $cookie_name = SN_COOKIE;
83
  protected $cookie_name_impersonate = SN_COOKIE_I;
84
  protected $secret_word = '';
85
86
  /**
87
   * @param string $filename
88
   */
89
  // OK v4.5
90
  public function __construct($filename = __FILE__) {
91
    parent::__construct($filename);
92
93
    $this->prepare();
94
95
    $this->manifest['active'] = false;
96
    if(!empty($this->config) && is_array($this->config['db'])) {
97
      // БД, отличная от стандартной
98
      $this->db = new db_mysql();
99
100
      $this->db->sn_db_connect($this->config['db']);
101
      if($this->manifest['active'] = $this->db->connected) {
102
        $this->provider_id = ACCOUNT_PROVIDER_CENTRAL;
103
104
        $this->domain = $this->config['domain'];
105
        $this->sn_root_path = $this->config['sn_root_path'];
106
        $this->cookie_name = $this->config['cookie_name'];
107
        $this->secret_word = $this->config['secretword'];
108
        // TODO Проверить наличие всех нужных таблиц
109
      } else {
110
        unset($this->db);
111
      }
112
    }
113
114
    // Fallback to local DB
115
    if(!$this->manifest['active']) {
116
      $this->db = classSupernova::$db;
117
118
      $this->provider_id = ACCOUNT_PROVIDER_LOCAL;
119
120
      $this->domain = null;
121
      $this->sn_root_path = SN_ROOT_RELATIVE;
122
      $this->cookie_name = SN_COOKIE;
123
      $this->secret_word = classSupernova::$sn_secret_word;
124
125
      $this->manifest['active'] = true;
126
    }
127
128
    $this->cookie_name_impersonate = $this->cookie_name . AUTH_COOKIE_IMPERSONATE_SUFFIX;
129
130
    $this->account = new Account($this->db);
131
    $this->confirmation = new Confirmation($this->db);
132
  }
133
134
  /**
135
   * Попытка залогиниться с использованием метода $method
136
   * @version 4.5
137
   *
138
   * @param string $method_name
139
   */
140
  // OK v4.5
141
  public function login() {
142
    // TODO Проверяем поддерживаемость метода
143
    // TODO Пытаемся залогиниться
144
    $this->password_reset_send_code();
145
    $this->password_reset_confirm();
146
    $this->register();
147
    $this->login_username();
148
    $this->login_cookie();
149
150
    return $this->account_login_status;
151
  }
152
153
  public function logout() {
154
    $this->cookie_clear();
155
  }
156
157
  /**
158
   * Меняет пароль у аккаунта с проверкой старого пароля
159
   *
160
   * @param      $old_password_unsafe
161
   * @param      $new_password_unsafe
162
   * @param null $salt_unsafe
163
   *
164
   * @return array|bool|resource
165
   */
166
  // OK v4.5
167
  public function password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe = null) {
168
    $result = parent::password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe);
169
    if($result) {
170
      $this->cookie_set();
171
    }
172
173
    return $result;
174
  }
175
176
  public function impersonate($account_to_impersonate) {
177
    $this->cookie_set($account_to_impersonate);
178
  }
179
180
  /**
181
   * @param Account $account
182
   */
183
  public function login_with_account($account) {
184
    $this->account = $account;
185
    $this->cookie_set();
186
    return $this->login_cookie();
187
  }
188
189
190
  /**
191
   * Отсылает письмо с кодом подтверждения для сброса пароля
192
   *
193
   * @return int|string
194
   */
195
  // OK v4.6
196
  protected function password_reset_send_code() {
197
    if(!$this->is_password_reset) {
198
      return $this->account_login_status;
199
    }
200
201
    // Проверяем поддержку сброса пароля
202
    if(!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {
203
      return $this->account_login_status;
204
    }
205
206
    try {
207
      $email_unsafe = $this->input_email_unsafe;
208
209
      unset($this->account);
210
      $this->account = new Account($this->db);
211
212
      if(!$this->account->db_get_by_email($email_unsafe)) {
213
        throw new Exception(PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS, ERR_ERROR);
214
        // return $this->account_login_status;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
215
      }
216
217
      $account_translation = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5
218
      $user_list = db_user_list_by_id(array_keys($account_translation));
219
220
      // TODO - Проверять уровень доступа аккаунта!
221
      // Аккаунты с АУТЛЕВЕЛ больше 0 - НЕ СБРАСЫВАЮТ ПАРОЛИ!
222
      foreach($user_list as $user_id => $user_data) {
223
        if($user_data['authlevel'] > AUTH_LEVEL_REGISTERED) {
224
          throw new Exception(PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT, ERR_ERROR);
225
        }
226
      }
227
228
      $confirmation = $this->confirmation->db_confirmation_get_latest_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
229
      if(isset($confirmation['create_time']) && SN_TIME_NOW - strtotime($confirmation['create_time']) < PERIOD_MINUTE_10) {
230
        throw new Exception(PASSWORD_RESTORE_ERROR_TOO_OFTEN, ERR_ERROR);
231
      }
232
233
      // Удаляем предыдущие записи продтверждения сброса пароля
234
      !empty($confirmation['id']) or $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
235
236
      sn_db_transaction_start();
237
      $confirm_code_unsafe = $this->confirmation->db_confirmation_get_unique_code_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
238
      sn_db_transaction_commit();
239
240
      @$result = mymail($email_unsafe,
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
241
        sprintf(classLocale::$lang['log_lost_email_title'], classSupernova::$config->game_name),
242
        sprintf(classLocale::$lang['log_lost_email_code'], SN_ROOT_VIRTUAL . 'login.php', $confirm_code_unsafe, date(FMT_DATE_TIME, SN_TIME_NOW + AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE), classSupernova::$config->game_name)
243
      );
244
245
      $result = $result ? PASSWORD_RESTORE_SUCCESS_CODE_SENT : PASSWORD_RESTORE_ERROR_SENDING;
246
    } catch(Exception $e) {
247
      sn_db_transaction_rollback();
248
      $result = $e->getMessage();
249
    }
250
251
    return $this->account_login_status = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result can also be of type string. However, the property $account_login_status is declared as type integer. 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...
252
  }
253
  /**
254
   * Сброс пароля по введенному коду подтверждения
255
   *
256
   * @return int|string
257
   */
258
  protected function password_reset_confirm() {
259
    if(!$this->is_password_reset_confirm) {
260
      return $this->account_login_status;
261
    }
262
263
    if($this->account_login_status != LOGIN_UNDEFINED) {
264
      return $this->account_login_status;
265
    }
266
267
    // Проверяем поддержку сброса пароля
268
    if(!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {
269
      return $this->account_login_status;
270
    }
271
272
    try {
273
      $code_unsafe = sys_get_param_str_unsafe('password_reset_code');
274
      if(empty($code_unsafe)) {
275
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_EMPTY, ERR_ERROR);
276
      }
277
278
      sn_db_transaction_start();
279
      $confirmation = $this->confirmation->db_confirmation_get_by_type_and_code(CONFIRM_PASSWORD_RESET, $code_unsafe); // OK 4.5
280
281
      if(empty($confirmation)) {
282
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_WRONG, ERR_ERROR);
283
      }
284
285
      if(SN_TIME_NOW - strtotime($confirmation['create_time']) > AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE) {
286
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_TOO_OLD, ERR_ERROR);
287
      }
288
289
      unset($this->account);
290
      $this->account = new Account($this->db);
291
292
      if(!$this->account->db_get_by_email($confirmation['email'])) {
293
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL, ERR_ERROR);
294
      }
295
296
      $new_password_unsafe = $this->make_random_password();
297
      $salt_unsafe = $this->password_salt_generate();
298
      if(!$this->account->db_set_password($new_password_unsafe, $salt_unsafe)) {
299
        // Ошибка смены пароля
300
        throw new Exception(AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE, ERR_ERROR);
301
      }
302
303
      $this->account_login_status = LOGIN_UNDEFINED;
304
      $this->remember_me = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $remember_me was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
305
      $this->cookie_set();
306
      $this->login_cookie();
307
308
      if($this->account_login_status == LOGIN_SUCCESS) {
309
        // TODO - НЕ ОБЯЗАТЕЛЬНО ОТПРАВЛЯТЬ ЧЕРЕЗ ЕМЕЙЛ! ЕСЛИ ЭТО ФЕЙСБУЧЕК ИЛИ ВКШЕЧКА - МОЖНО ЧЕРЕЗ ЛС ПИСАТЬ!!
310
        $message_header = sprintf(classLocale::$lang['log_lost_email_title'], classSupernova::$config->game_name);
311
        $message = sprintf(classLocale::$lang['log_lost_email_pass'], classSupernova::$config->game_name, $this->account->account_name, $new_password_unsafe);
312
        @$operation_result = mymail($confirmation['email'], $message_header, htmlspecialchars($message));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
313
314
        $users_translated = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5
315
        if(!empty($users_translated)) {
316
          // Отправляем в лички письмо о сбросе пароля
317
318
          // ПО ОПРЕДЕЛЕНИЮ в $users_translated только
319
          //    - аккаунты, поддерживающие сброс пароля
320
          //    - список аккаунтов, имеющих тот же емейл, что указан в Подтверждении
321
          //    - игроки, привязанные только к этим аккаунтам
322
          // Значит им всем сразу скопом можно отправлять сообщения
323
          $message = sprintf(classLocale::$lang['sys_password_reset_message_body'], $new_password_unsafe);
324
          $message = sys_bbcodeParse($message) . '<br><br>';
325
326
          foreach($users_translated as $user_id => $providers_list) {
327
            msg_send_simple_message($user_id, 0, SN_TIME_NOW, MSG_TYPE_ADMIN, classLocale::$lang['sys_administration'], classLocale::$lang['sys_login_register_message_title'], $message);
328
          }
329
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
330
          // Фигня - может быть и пустой, если у нас есть только аккаунт, но нет пользователей
331
          // throw new Exception(AUTH_PASSWORD_RESET_INSIDE_ERROR_NO_ACCOUNT_FOR_CONFIRMATION, ERR_ERROR);
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
332
        }
333
      }
334
335
      $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $confirmation['email']); // OK 4.5
336
337
      sn_db_transaction_commit();
338
339
      sys_redirect('overview.php');
340
    } catch (Exception $e) {
341
      sn_db_transaction_rollback();
342
      $this->account_login_status = $e->getMessage();
0 ignored issues
show
Documentation Bug introduced by
The property $account_login_status was declared of type integer, but $e->getMessage() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
343
    }
344
345
    return $this->account_login_status;
346
  }
347
348
  /**
349
   * Функция инициализирует данные провайдера - разворачивает куки, берет данные итд
350
   */
351
  // OK v4.5
352
  protected function prepare() {
353
    $this->input_login_unsafe = sys_get_param_str_unsafe('username', sys_get_param_str_unsafe('email')); // TODO переделать эту порнографию
354
355
    $this->is_login = sys_get_param('login') ? true : false;
356
    $this->is_register = sys_get_param('register') ? true : false;
357
    $this->is_password_reset = sys_get_param('password_reset') ? true : false;
358
    $this->is_password_reset_confirm = sys_get_param('password_reset_confirm') ? true : false;
359
360
    $this->remember_me = intval(sys_get_param_int('rememberme') || $this->is_register);
0 ignored issues
show
Documentation Bug introduced by
The property $remember_me was declared of type boolean, but intval(sys_get_param_int... || $this->is_register) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
361
    $this->input_login_password_raw = sys_get_param('password');
0 ignored issues
show
Documentation Bug introduced by
It seems like sys_get_param('password') can also be of type array. However, the property $input_login_password_raw 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...
362
    $this->input_login_password_raw_repeat = sys_get_param('password_repeat');
0 ignored issues
show
Documentation Bug introduced by
It seems like sys_get_param('password_repeat') can also be of type array. However, the property $input_login_password_raw_repeat 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...
363
    $this->input_email_unsafe = sys_get_param_str_unsafe('email');
364
    $this->input_language_unsafe = sys_get_param_str_unsafe('lang', DEFAULT_LANG);
365
    $this->input_language_safe = sys_get_param_str('lang', DEFAULT_LANG);
366
367
  }
368
369
  /**
370
   * Пытается зарегестрировать пользователя по введенным данным
371
   * @version 4.5
372
   *
373
   * @return mixed
374
   */
375
  // OK v4.5
376
  protected function register() {
377
    // TODO РЕГИСТРАЦИЯ ВСЕГДА ДОЛЖНА ЛОГИНИТЬ ПОЛЬЗОВАТЕЛЯ!
378
    $this->flog('Регистрация: начинаем. Провайдер ' . $this->provider_id);
379
380
    try {
381
      if(!$this->is_register) {
382
        $this->flog('Регистрация: не выставлен флаг регистрации - пропускаем');
383
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
384
      }
385
386
      $this->register_validate_input();
387
388
      sn_db_transaction_start();
389
390
      $this->account->db_get_by_name_or_email($this->input_login_unsafe, $this->input_email_unsafe);
391
      if($this->account->is_exists) {
392
        if($this->account->account_email == $this->input_email_unsafe) {
393
          throw new Exception(REGISTER_ERROR_EMAIL_EXISTS, ERR_ERROR);
394
        } else {
395
          throw new Exception(REGISTER_ERROR_ACCOUNT_NAME_EXISTS, ERR_ERROR);
396
        }
397
      }
398
399
      // Проблемы с созданием аккаунта - вызовут эксершн и обработается catch()
400
      $this->account->db_create(
401
        $this->input_login_unsafe,
402
        $this->input_login_password_raw,
403
        $this->input_email_unsafe,
404
        $this->input_language_unsafe
405
      );
406
407
      // Устанавливать не надо - мы дальше пойдем по workflow
408
      $this->account_login_status = LOGIN_SUCCESS;
409
      $this->cookie_set();
410
411
      // А вот это пока не нужно. Трансляцией аккаунтов в юзеров и созданием новых юзеров для новозашедших аккаунтов занимается Auth
412
      // $this->register_account();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
413
      sn_db_transaction_commit();
414
    } catch(Exception $e) {
415
      sn_db_transaction_rollback();
416
      $this->account_login_status == LOGIN_UNDEFINED ? $this->account_login_status = $e->getMessage() : false;
0 ignored issues
show
Documentation Bug introduced by
The property $account_login_status was declared of type integer, but $e->getMessage() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
417
    }
418
419
    return $this->account_login_status;
420
  }
421
422
  /**
423
   * Пытается залогинить пользователя по куке
424
   * @version 4.5
425
   *
426
   * @return int Результат попытки
427
   */
428
  // OK v4.5
429
  protected function login_cookie() {
430
    if($this->account_login_status != LOGIN_UNDEFINED) {
431
      return $this->account_login_status;
432
    }
433
434
    // Пытаемся войти по куке
435
    if(!empty($_COOKIE[$this->cookie_name])) {
436
      if(count(explode("/%/", $_COOKIE[$this->cookie_name])) < 4) {
437
        list($account_id_unsafe, $cookie_password_hash_salted, $user_remember_me) = explode(AUTH_COOKIE_DELIMETER, $_COOKIE[$this->cookie_name]);
438
      } else {
439
        list($account_id_unsafe, $user_name, $cookie_password_hash_salted, $user_remember_me) = explode("/%/", $_COOKIE[$this->cookie_name]);
0 ignored issues
show
Unused Code introduced by
The assignment to $user_name is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
440
      }
441
442
      if(
443
        $this->account->db_get_by_id($account_id_unsafe)
444
        && ($this->password_encode_for_cookie($this->account->account_password) == $cookie_password_hash_salted)
445
      ) {
446
        $this->account_login_status = LOGIN_SUCCESS;
447
        $this->remember_me = intval($user_remember_me);
0 ignored issues
show
Documentation Bug introduced by
The property $remember_me was declared of type boolean, but intval($user_remember_me) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
448
      }
449
    }
450
451
    if($this->account_login_status != LOGIN_SUCCESS) {
452
      // Невалидная кука - чистим
453
      $this->cookie_clear();
454
    }
455
456
    return $this->account_login_status;
457
  }
458
459
  /**
460
   * Пытается залогинить пользователя по имени аккаунта и паролю
461
   * @version 4.5
462
   *
463
   * @return mixed
464
   */
465
  // OK v4.5
466
  protected function login_username() {
467
    // TODO - Логин по старым именам
468
    try {
469
      if(!$this->is_login) {
470
        $this->flog('Логин: не выставлен флаг входа в игру - это не логин');
471
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
472
      }
473
474
      // TODO Пустое имя аккаунта
475
      if(!$this->input_login_unsafe) {
476
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
477
      }
478
479
      $this->login_validate_input();
480
481
      if(!$this->account->db_get_by_name($this->input_login_unsafe) && !$this->account->db_get_by_email($this->input_login_unsafe)) {
482
        throw new Exception(LOGIN_ERROR_USERNAME, ERR_ERROR);
483
      }
484
485
      if(!$this->account->password_check($this->input_login_password_raw)) {
486
        throw new Exception(LOGIN_ERROR_PASSWORD, ERR_ERROR);
487
      }
488
489
      $this->cookie_set();
490
      $this->account_login_status = LOGIN_SUCCESS;
491
    } catch(Exception $e) {
492
      $this->account_login_status == LOGIN_UNDEFINED ? $this->account_login_status = $e->getMessage() : false;
0 ignored issues
show
Documentation Bug introduced by
The property $account_login_status was declared of type integer, but $e->getMessage() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
493
    }
494
495
    return $this->account_login_status;
496
  }
497
498
  /**
499
   * Устанавливает куку аккаунта по данным $this->data[F_ACCOUNT]
500
   *
501
   * @param Account|null $account_to_impersonate
502
   *
503
   * @return bool
504
   * @throws Exception
505
   *
506
   */
507
  // OK v4.5
508
  // TODO - должен устанавливать куку исходя из пользователя, что бы пользователь мог логинится
509
  // TODO - или ставить мультикуку - хотя нахуя, спрашивается
510
  protected function cookie_set($account_to_impersonate = null) {
511
    $this_account = is_object($account_to_impersonate) ? $account_to_impersonate : $this->account;
512
513
    if(!is_object($this_account) || !$this_account->is_exists) {
514
      throw new Exception(LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET, ERR_ERROR);
515
    }
516
517
    if(is_object($account_to_impersonate) && $account_to_impersonate->is_exists) {
518
      sn_setcookie($this->cookie_name_impersonate, $_COOKIE[$this->cookie_name], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $this->domain);
519
    }
520
521
    $expire_time = $this->remember_me ? SN_TIME_NOW + PERIOD_YEAR : 0;
522
523
    $password_encoded = $this->password_encode_for_cookie($this_account->account_password);
524
    $cookie = $this_account->account_id . AUTH_COOKIE_DELIMETER . $password_encoded . AUTH_COOKIE_DELIMETER . $this->remember_me;
525
    $this->flog("cookie_set() - Устанавливаем куку {$cookie}");
526
    return sn_setcookie($this->cookie_name, $cookie, $expire_time, $this->sn_root_path, $this->domain);
527
  }
528
529
  /**
530
   * Очищает куку аккаунта - совсем или восстанавливая куку текущего имперсонатора
531
   */
532
  // OK v4.1
533
  protected function cookie_clear() {
534
    // Автоматически вообще-то - если установлена кука имперсонатора - то чистим обычную, а куку имперсонатора - копируем в неё
535
    if(!empty($_COOKIE[$this->cookie_name_impersonate])) {
536
      sn_setcookie($this->cookie_name, $_COOKIE[$this->cookie_name_impersonate], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $this->domain);
537
      sn_setcookie($this->cookie_name_impersonate, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $this->domain);
538
    } else {
539
      sn_setcookie($this->cookie_name, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $this->domain);
540
    }
541
  }
542
543
544
  // ХЕЛПЕРЫ ===========================================================================================================
545
  /**
546
   * Проверяет введенные данные логина на корректность
547
   *
548
   * @throws Exception
549
   */
550
  // OK v4.1
551
  protected function login_validate_input() {
552
    // Проверяем, что бы в начале и конце не было пустых символов
553
    // TODO - при копировании Эксель -> Опера - в конце образуются пустые места. Это не должно быть проблемой! Вынести проверку пароля в регистрацию!
554
    if($this->input_login_password_raw != trim($this->input_login_password_raw)) {
555
      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);
556
    }
557
    if(!$this->input_login_password_raw) {
558
      throw new Exception(LOGIN_ERROR_PASSWORD_EMPTY, ERR_ERROR);
559
    }
560
  }
561
562
  /**
563
   * Проверяет данные для регистрации на корректность
564
   *
565
   * @throws Exception
566
   */
567
  // OK v4.5
568
  protected function register_validate_input() {
569
    // То, что не подходит для логина - не подходит и для регистрации
570
    $this->login_validate_input();
571
572
    // Если нет имени пользователя - NO GO!
573
    if(!$this->input_login_unsafe) {
574
      throw new Exception(LOGIN_ERROR_USERNAME_EMPTY, ERR_ERROR);
575
    }
576
    // Если логин имеет запрещенные символы - NO GO!
577
    if(strpbrk($this->input_login_unsafe, LOGIN_REGISTER_CHARACTERS_PROHIBITED)) {
578
      throw new Exception(LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS, ERR_ERROR);
579
    }
580
    // Если логин меньше минимальной длины - NO GO!
581
    if(strlen($this->input_login_unsafe) < LOGIN_LENGTH_MIN) {
582
      throw new Exception(REGISTER_ERROR_USERNAME_SHORT, ERR_ERROR);
583
    }
584
    // Если пароль меньше минимальной длины - NO GO!
585
    if(strlen($this->input_login_password_raw) < PASSWORD_LENGTH_MIN) {
586
      throw new Exception(REGISTER_ERROR_PASSWORD_INSECURE, ERR_ERROR);
587
    }
588
    // Если пароль имеет пробельные символы в начале или конце - NO GO!
589
    if($this->input_login_password_raw != trim($this->input_login_password_raw)) {
590
      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);
591
    }
592
    // Если пароль не совпадает с подтверждением - NO GO! То, что у пароля нет пробельных символов в начале/конце - мы уже проверили выше
593
    //Если они есть у повтора - значит пароль и повтор не совпадут
594
    if($this->input_login_password_raw <> $this->input_login_password_raw_repeat) {
595
      throw new Exception(REGISTER_ERROR_PASSWORD_DIFFERENT, ERR_ERROR);
596
    }
597
    // Если нет емейла - NO GO!
598
    // TODO - регистрация без емейла
599
    if(!$this->input_email_unsafe) {
600
      throw new Exception(REGISTER_ERROR_EMAIL_EMPTY, ERR_ERROR);
601
    }
602
    // Если емейл не является емейлом - NO GO!
603
    if(!is_email($this->input_email_unsafe)) {
604
      throw new Exception(REGISTER_ERROR_EMAIL_WRONG, ERR_ERROR);
605
    }
606
  }
607
608
609
610
  // OK v4
611
  protected function password_encode_for_cookie($password) {
612
    return md5("{$password}--" . $this->secret_word);
613
  }
614
  // OK v4
615
  protected function password_encode($password, $salt) {
616
    return core_auth::password_encode($password, $salt);
617
  }
618
  // OK v4
619
  protected function password_salt_generate() {
620
    return core_auth::password_salt_generate();
621
  }
622
  /**
623
   * Генерирует случайный пароль
624
   *
625
   * @return string
626
   */
627
  // OK v4
628
  protected function make_random_password() {
629
    return core_auth::make_random_password();
630
  }
631 View Code Duplication
  protected function flog($message, $die = false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
632
    if(!defined('DEBUG_AUTH') || !DEBUG_AUTH) {
633
      return;
634
    }
635
    list($called, $caller) = debug_backtrace(false);
636
637
    $caller_name =
638
      ((get_called_class()) ? get_called_class() : (!empty($caller['class']) ? $caller['class'] : '')) .
639
      (!empty($caller['type']) ? $caller['type'] : '') .
640
      (!empty($caller['function']) ? $caller['function'] : '') .
641
      (!empty($called['line']) ? ':' . $called['line'] : '');
642
643
    $_SERVER['SERVER_NAME'] == 'localhost' ? print("<div class='debug'>$message - $caller_name\r\n</div>") : false;
644
645
    classSupernova::log_file("$message - $caller_name");
646
    if($die) {
647
      $die && die("<div class='negative'>СТОП! Функция {$caller_name} при вызове в " . get_called_class() . " (располагается в " . get_class() . "). СООБЩИТЕ АДМИНИСТРАЦИИ!</div>");
648
    }
649
  }
650
651
}
652