Completed
Push — work-fleets ( 2bd11a...17dd3b )
by SuperNova.WS
06:36
created

auth_local::prepare()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 12
c 0
b 0
f 0
nc 16
nop 0
dl 0
loc 16
rs 8.8571
ccs 0
cts 12
cp 0
crap 42
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" #41a50.104# 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();
0 ignored issues
show
Bug introduced by
The call to db_mysql::__construct() misses a required argument $gc.

This check looks for function calls that miss required arguments.

Loading history...
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
   *
137
   * @param string $method_name
0 ignored issues
show
Bug introduced by
There is no parameter named $method_name. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
138
   */
139
  public function login() {
140
    // TODO Проверяем поддерживаемость метода
141
    // TODO Пытаемся залогиниться
142
    $this->password_reset_send_code();
143
    $this->password_reset_confirm();
144
    $this->register();
145
    $this->login_username();
146
    $this->login_cookie();
147
148
    return $this->account_login_status;
149
  }
150
151
  public function logout() {
152
    $this->cookie_clear();
153
  }
154
155
  /**
156
   * Меняет пароль у аккаунта с проверкой старого пароля
157
   *
158
   * @param      $old_password_unsafe
159
   * @param      $new_password_unsafe
160
   * @param null $salt_unsafe
161
   *
162
   * @return array|bool|resource
163
   */
164
  // OK v4.5
165
  public function password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe = null) {
166
    $result = parent::password_change($old_password_unsafe, $new_password_unsafe, $salt_unsafe);
167
    if($result) {
168
      $this->cookie_set();
169
    }
170
171
    return $result;
172
  }
173
174
  public function impersonate($account_to_impersonate) {
175
    $this->cookie_set($account_to_impersonate);
176
  }
177
178
  /**
179
   * @param Account $account
180
   */
181
  public function login_with_account($account) {
182
    $this->account = $account;
183
    $this->cookie_set();
184
    return $this->login_cookie();
185
  }
186
187
188
  /**
189
   * Отсылает письмо с кодом подтверждения для сброса пароля
190
   *
191
   * @return int|string
192
   */
193
  // OK v4.6
194
  protected function password_reset_send_code() {
195
    if(!$this->is_password_reset) {
196
      return $this->account_login_status;
197
    }
198
199
    // Проверяем поддержку сброса пароля
200
    if(!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {
201
      return $this->account_login_status;
202
    }
203
204
    try {
205
      $email_unsafe = $this->input_email_unsafe;
206
207
      unset($this->account);
208
      $this->account = new Account($this->db);
209
210
      if(!$this->account->db_get_by_email($email_unsafe)) {
211
        throw new Exception(PASSWORD_RESTORE_ERROR_EMAIL_NOT_EXISTS, ERR_ERROR);
212
        // 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...
213
      }
214
215
      $account_translation = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5
216
      $user_list = DBStaticUser::db_user_list_by_id(array_keys($account_translation));
217
218
      // TODO - Проверять уровень доступа аккаунта!
219
      // Аккаунты с АУТЛЕВЕЛ больше 0 - НЕ СБРАСЫВАЮТ ПАРОЛИ!
220
      foreach($user_list as $user_id => $user_data) {
221
        if($user_data['authlevel'] > AUTH_LEVEL_REGISTERED) {
222
          throw new Exception(PASSWORD_RESTORE_ERROR_ADMIN_ACCOUNT, ERR_ERROR);
223
        }
224
      }
225
226
      $confirmation = $this->confirmation->db_confirmation_get_latest_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
227
      if(isset($confirmation['create_time']) && SN_TIME_NOW - strtotime($confirmation['create_time']) < PERIOD_MINUTE_10) {
228
        throw new Exception(PASSWORD_RESTORE_ERROR_TOO_OFTEN, ERR_ERROR);
229
      }
230
231
      // Удаляем предыдущие записи продтверждения сброса пароля
232
      !empty($confirmation['id']) or $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
233
234
      sn_db_transaction_start();
235
      $confirm_code_unsafe = $this->confirmation->db_confirmation_get_unique_code_by_type_and_email(CONFIRM_PASSWORD_RESET, $email_unsafe); // OK 4.5
236
      sn_db_transaction_commit();
237
238
      $result = mymail($email_unsafe,
239
        sprintf(classLocale::$lang['log_lost_email_title'], classSupernova::$config->game_name),
240
        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)
241
      );
242
243
      $result = $result ? PASSWORD_RESTORE_SUCCESS_CODE_SENT : PASSWORD_RESTORE_ERROR_SENDING;
244
    } catch(Exception $e) {
245
      sn_db_transaction_rollback();
246
      $result = $e->getMessage();
247
    }
248
249
    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...
250
  }
251
  /**
252
   * Сброс пароля по введенному коду подтверждения
253
   *
254
   * @return int|string
255
   */
256
  protected function password_reset_confirm() {
257
    if(!$this->is_password_reset_confirm) {
258
      return $this->account_login_status;
259
    }
260
261
    if($this->account_login_status != LOGIN_UNDEFINED) {
262
      return $this->account_login_status;
263
    }
264
265
    // Проверяем поддержку сброса пароля
266
    if(!$this->is_feature_supported(AUTH_FEATURE_PASSWORD_RESET)) {
267
      return $this->account_login_status;
268
    }
269
270
    try {
271
      $code_unsafe = sys_get_param_str_unsafe('password_reset_code');
272
      if(empty($code_unsafe)) {
273
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_EMPTY, ERR_ERROR);
274
      }
275
276
      sn_db_transaction_start();
277
      $confirmation = $this->confirmation->db_confirmation_get_by_type_and_code(CONFIRM_PASSWORD_RESET, $code_unsafe); // OK 4.5
278
279
      if(empty($confirmation)) {
280
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_WRONG, ERR_ERROR);
281
      }
282
283
      if(SN_TIME_NOW - strtotime($confirmation['create_time']) > AUTH_PASSWORD_RESET_CONFIRMATION_EXPIRE) {
284
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_TOO_OLD, ERR_ERROR);
285
      }
286
287
      unset($this->account);
288
      $this->account = new Account($this->db);
289
290
      if(!$this->account->db_get_by_email($confirmation['email'])) {
291
        throw new Exception(PASSWORD_RESTORE_ERROR_CODE_OK_BUT_NO_ACCOUNT_FOR_EMAIL, ERR_ERROR);
292
      }
293
294
      $new_password_unsafe = $this->make_random_password();
295
      $salt_unsafe = $this->password_salt_generate();
296
      if(!$this->account->db_set_password($new_password_unsafe, $salt_unsafe)) {
297
        // Ошибка смены пароля
298
        throw new Exception(AUTH_ERROR_INTERNAL_PASSWORD_CHANGE_ON_RESTORE, ERR_ERROR);
299
      }
300
301
      $this->account_login_status = LOGIN_UNDEFINED;
302
      $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...
303
      $this->cookie_set();
304
      $this->login_cookie();
305
306
      if($this->account_login_status == LOGIN_SUCCESS) {
307
        // TODO - НЕ ОБЯЗАТЕЛЬНО ОТПРАВЛЯТЬ ЧЕРЕЗ ЕМЕЙЛ! ЕСЛИ ЭТО ФЕЙСБУЧЕК ИЛИ ВКШЕЧКА - МОЖНО ЧЕРЕЗ ЛС ПИСАТЬ!!
308
        $message_header = sprintf(classLocale::$lang['log_lost_email_title'], classSupernova::$config->game_name);
309
        $message = sprintf(classLocale::$lang['log_lost_email_pass'], classSupernova::$config->game_name, $this->account->account_name, $new_password_unsafe);
310
        // TODO - use $operation_result =
311
        mymail($confirmation['email'], $message_header, htmlspecialchars($message));
312
313
        $users_translated = PlayerToAccountTranslate::db_translate_get_users_from_account_list($this->provider_id, $this->account->account_id); // OK 4.5
314
        if(!empty($users_translated)) {
315
          // Отправляем в лички письмо о сбросе пароля
316
317
          // ПО ОПРЕДЕЛЕНИЮ в $users_translated только
318
          //    - аккаунты, поддерживающие сброс пароля
319
          //    - список аккаунтов, имеющих тот же емейл, что указан в Подтверждении
320
          //    - игроки, привязанные только к этим аккаунтам
321
          // Значит им всем сразу скопом можно отправлять сообщения
322
          $message = sprintf(classLocale::$lang['sys_password_reset_message_body'], $new_password_unsafe);
323
          $message = sys_bbcodeParse($message) . '<br><br>';
324
325
          foreach($users_translated as $user_id => $providers_list) {
326
            DBStaticMessages::msgSendFromAdmin($user_id, classLocale::$lang['sys_login_register_message_title'], $message);
327
          }
328
        } 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...
329
          // Фигня - может быть и пустой, если у нас есть только аккаунт, но нет пользователей
330
          // 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...
331
        }
332
      }
333
334
      $this->confirmation->db_confirmation_delete_by_type_and_email(CONFIRM_PASSWORD_RESET, $confirmation['email']); // OK 4.5
335
336
      sn_db_transaction_commit();
337
338
      sys_redirect('overview.php');
339
    } catch (Exception $e) {
340
      sn_db_transaction_rollback();
341
      $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...
342
    }
343
344
    return $this->account_login_status;
345
  }
346
347
  /**
348
   * Функция инициализирует данные провайдера - разворачивает куки, берет данные итд
349
   */
350
  // OK v4.5
351
  protected function prepare() {
352
    $this->input_login_unsafe = sys_get_param_str_unsafe('username', sys_get_param_str_unsafe('email')); // TODO переделать эту порнографию
353
354
    $this->is_login = sys_get_param('login') ? true : false;
355
    $this->is_register = sys_get_param('register') ? true : false;
356
    $this->is_password_reset = sys_get_param('password_reset') ? true : false;
357
    $this->is_password_reset_confirm = sys_get_param('password_reset_confirm') ? true : false;
358
359
    $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...
360
    $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...
361
    $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...
362
    $this->input_email_unsafe = sys_get_param_str_unsafe('email');
363
    $this->input_language_unsafe = sys_get_param_str_unsafe('lang', DEFAULT_LANG);
364
    $this->input_language_safe = sys_get_param_str('lang', DEFAULT_LANG);
365
366
  }
367
368
  /**
369
   * Пытается зарегестрировать пользователя по введенным данным
370
   *
371
   * @return mixed
372
   */
373
  protected function register() {
374
    // TODO РЕГИСТРАЦИЯ ВСЕГДА ДОЛЖНА ЛОГИНИТЬ ПОЛЬЗОВАТЕЛЯ!
375
    $this->flog('Регистрация: начинаем. Провайдер ' . $this->provider_id);
376
377
    try {
378
      if(!$this->is_register) {
379
        $this->flog('Регистрация: не выставлен флаг регистрации - пропускаем');
380
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
381
      }
382
383
      $this->register_validate_input();
384
385
      sn_db_transaction_start();
386
387
      $this->account->db_get_by_name_or_email($this->input_login_unsafe, $this->input_email_unsafe);
388
      if($this->account->is_exists) {
389
        if($this->account->account_email == $this->input_email_unsafe) {
390
          throw new Exception(REGISTER_ERROR_EMAIL_EXISTS, ERR_ERROR);
391
        } else {
392
          throw new Exception(REGISTER_ERROR_ACCOUNT_NAME_EXISTS, ERR_ERROR);
393
        }
394
      }
395
396
      // Проблемы с созданием аккаунта - вызовут эксершн и обработается catch()
397
      $this->account->db_create(
398
        $this->input_login_unsafe,
399
        $this->input_login_password_raw,
400
        $this->input_email_unsafe,
401
        $this->input_language_unsafe
402
      );
403
404
      // Устанавливать не надо - мы дальше пойдем по workflow
405
      $this->account_login_status = LOGIN_SUCCESS;
406
      $this->cookie_set();
407
408
      // А вот это пока не нужно. Трансляцией аккаунтов в юзеров и созданием новых юзеров для новозашедших аккаунтов занимается Auth
409
      // $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...
410
      sn_db_transaction_commit();
411
    } catch(Exception $e) {
412
      sn_db_transaction_rollback();
413
      $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...
414
    }
415
416
    return $this->account_login_status;
417
  }
418
419
  /**
420
   * Пытается залогинить пользователя по куке
421
   *
422
   * @return int Результат попытки
423
   */
424
  protected function login_cookie() {
425
    if($this->account_login_status != LOGIN_UNDEFINED) {
426
      return $this->account_login_status;
427
    }
428
429
    // Пытаемся войти по куке
430
    if(!empty($_COOKIE[$this->cookie_name])) {
431
      if(count(explode("/%/", $_COOKIE[$this->cookie_name])) < 4) {
432
        list($account_id_unsafe, $cookie_password_hash_salted, $user_remember_me) = explode(AUTH_COOKIE_DELIMETER, $_COOKIE[$this->cookie_name]);
433
      } else {
434
        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...
435
      }
436
437
      if(
438
        $this->account->db_get_by_id($account_id_unsafe)
439
        && ($this->password_encode_for_cookie($this->account->account_password) == $cookie_password_hash_salted)
440
      ) {
441
        $this->account_login_status = LOGIN_SUCCESS;
442
        $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...
443
      }
444
    }
445
446
    if($this->account_login_status != LOGIN_SUCCESS) {
447
      // Невалидная кука - чистим
448
      $this->cookie_clear();
449
    }
450
451
    return $this->account_login_status;
452
  }
453
454
  /**
455
   * Пытается залогинить пользователя по имени аккаунта и паролю
456
   *
457
   * @return mixed
458
   */
459
  // OK v4.5
460
  protected function login_username() {
461
    // TODO - Логин по старым именам
462
    try {
463
      if(!$this->is_login) {
464
        $this->flog('Логин: не выставлен флаг входа в игру - это не логин');
465
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
466
      }
467
468
      // TODO Пустое имя аккаунта
469
      if(!$this->input_login_unsafe) {
470
        throw new Exception(LOGIN_UNDEFINED, ERR_ERROR);
471
      }
472
473
      $this->login_validate_input();
474
475
      if(!$this->account->db_get_by_name($this->input_login_unsafe) && !$this->account->db_get_by_email($this->input_login_unsafe)) {
476
        throw new Exception(LOGIN_ERROR_USERNAME, ERR_ERROR);
477
      }
478
479
      if(!$this->account->password_check($this->input_login_password_raw)) {
480
        throw new Exception(LOGIN_ERROR_PASSWORD, ERR_ERROR);
481
      }
482
483
      $this->cookie_set();
484
      $this->account_login_status = LOGIN_SUCCESS;
485
    } catch(Exception $e) {
486
      $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...
487
    }
488
489
    return $this->account_login_status;
490
  }
491
492
  /**
493
   * Устанавливает куку аккаунта по данным $this->data[F_ACCOUNT]
494
   *
495
   * @param Account|null $account_to_impersonate
496
   *
497
   * @return bool
498
   * @throws Exception
499
   *
500
   */
501
  // OK v4.5
502
  // TODO - должен устанавливать куку исходя из пользователя, что бы пользователь мог логинится
503
  // TODO - или ставить мультикуку - хотя нахуя, спрашивается
504
  protected function cookie_set($account_to_impersonate = null) {
505
    $this_account = is_object($account_to_impersonate) ? $account_to_impersonate : $this->account;
506
507
    if(!is_object($this_account) || !$this_account->is_exists) {
508
      throw new Exception(LOGIN_ERROR_NO_ACCOUNT_FOR_COOKIE_SET, ERR_ERROR);
509
    }
510
511
    if(is_object($account_to_impersonate) && $account_to_impersonate->is_exists) {
512
      sn_setcookie($this->cookie_name_impersonate, $_COOKIE[$this->cookie_name], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $this->domain);
513
    }
514
515
    $expire_time = $this->remember_me ? SN_TIME_NOW + PERIOD_YEAR : 0;
516
517
    $password_encoded = $this->password_encode_for_cookie($this_account->account_password);
518
    $cookie = $this_account->account_id . AUTH_COOKIE_DELIMETER . $password_encoded . AUTH_COOKIE_DELIMETER . $this->remember_me;
519
    $this->flog("cookie_set() - Устанавливаем куку {$cookie}");
520
    return sn_setcookie($this->cookie_name, $cookie, $expire_time, $this->sn_root_path, $this->domain);
521
  }
522
523
  /**
524
   * Очищает куку аккаунта - совсем или восстанавливая куку текущего имперсонатора
525
   */
526
  // OK v4.1
527
  protected function cookie_clear() {
528
    // Автоматически вообще-то - если установлена кука имперсонатора - то чистим обычную, а куку имперсонатора - копируем в неё
529
    if(!empty($_COOKIE[$this->cookie_name_impersonate])) {
530
      sn_setcookie($this->cookie_name, $_COOKIE[$this->cookie_name_impersonate], SN_TIME_NOW + PERIOD_YEAR, $this->sn_root_path, $this->domain);
531
      sn_setcookie($this->cookie_name_impersonate, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $this->domain);
532
    } else {
533
      sn_setcookie($this->cookie_name, '', SN_TIME_NOW - PERIOD_WEEK, $this->sn_root_path, $this->domain);
534
    }
535
  }
536
537
538
  // ХЕЛПЕРЫ ===========================================================================================================
539
  /**
540
   * Проверяет введенные данные логина на корректность
541
   *
542
   * @throws Exception
543
   */
544
  // OK v4.1
545
  protected function login_validate_input() {
546
    // Проверяем, что бы в начале и конце не было пустых символов
547
    // TODO - при копировании Эксель -> Опера - в конце образуются пустые места. Это не должно быть проблемой! Вынести проверку пароля в регистрацию!
548
    if($this->input_login_password_raw != trim($this->input_login_password_raw)) {
549
      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);
550
    }
551
    if(!$this->input_login_password_raw) {
552
      throw new Exception(LOGIN_ERROR_PASSWORD_EMPTY, ERR_ERROR);
553
    }
554
  }
555
556
  /**
557
   * Проверяет данные для регистрации на корректность
558
   *
559
   * @throws Exception
560
   */
561
  // OK v4.5
562
  protected function register_validate_input() {
563
    // То, что не подходит для логина - не подходит и для регистрации
564
    $this->login_validate_input();
565
566
    // Если нет имени пользователя - NO GO!
567
    if(!$this->input_login_unsafe) {
568
      throw new Exception(LOGIN_ERROR_USERNAME_EMPTY, ERR_ERROR);
569
    }
570
    // Если логин имеет запрещенные символы - NO GO!
571
    if(strpbrk($this->input_login_unsafe, LOGIN_REGISTER_CHARACTERS_PROHIBITED)) {
572
      throw new Exception(LOGIN_ERROR_USERNAME_RESTRICTED_CHARACTERS, ERR_ERROR);
573
    }
574
    // Если логин меньше минимальной длины - NO GO!
575
    if(strlen($this->input_login_unsafe) < LOGIN_LENGTH_MIN) {
576
      throw new Exception(REGISTER_ERROR_USERNAME_SHORT, ERR_ERROR);
577
    }
578
    // Если пароль меньше минимальной длины - NO GO!
579
    if(strlen($this->input_login_password_raw) < PASSWORD_LENGTH_MIN) {
580
      throw new Exception(REGISTER_ERROR_PASSWORD_INSECURE, ERR_ERROR);
581
    }
582
    // Если пароль имеет пробельные символы в начале или конце - NO GO!
583
    if($this->input_login_password_raw != trim($this->input_login_password_raw)) {
584
      throw new Exception(LOGIN_ERROR_PASSWORD_TRIMMED, ERR_ERROR);
585
    }
586
    // Если пароль не совпадает с подтверждением - NO GO! То, что у пароля нет пробельных символов в начале/конце - мы уже проверили выше
587
    //Если они есть у повтора - значит пароль и повтор не совпадут
588
    if($this->input_login_password_raw <> $this->input_login_password_raw_repeat) {
589
      throw new Exception(REGISTER_ERROR_PASSWORD_DIFFERENT, ERR_ERROR);
590
    }
591
    // Если нет емейла - NO GO!
592
    // TODO - регистрация без емейла
593
    if(!$this->input_email_unsafe) {
594
      throw new Exception(REGISTER_ERROR_EMAIL_EMPTY, ERR_ERROR);
595
    }
596
    // Если емейл не является емейлом - NO GO!
597
    if(!is_email($this->input_email_unsafe)) {
598
      throw new Exception(REGISTER_ERROR_EMAIL_WRONG, ERR_ERROR);
599
    }
600
  }
601
602
603
604
  // OK v4
605
  protected function password_encode_for_cookie($password) {
606
    return md5("{$password}--" . $this->secret_word);
607
  }
608
  // OK v4
609
  protected function password_encode($password, $salt) {
610
    return core_auth::password_encode($password, $salt);
611
  }
612
  // OK v4
613
  protected function password_salt_generate() {
614
    return core_auth::password_salt_generate();
615
  }
616
  /**
617
   * Генерирует случайный пароль
618
   *
619
   * @return string
620
   */
621
  // OK v4
622
  protected function make_random_password() {
623
    return core_auth::make_random_password();
624
  }
625 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...
626
    if(!defined('DEBUG_AUTH') || !DEBUG_AUTH) {
627
      return;
628
    }
629
    list($called, $caller) = debug_backtrace(false);
630
631
    $caller_name =
632
      ((get_called_class()) ? get_called_class() : (!empty($caller['class']) ? $caller['class'] : '')) .
633
      (!empty($caller['type']) ? $caller['type'] : '') .
634
      (!empty($caller['function']) ? $caller['function'] : '') .
635
      (!empty($called['line']) ? ':' . $called['line'] : '');
636
637
    $_SERVER['SERVER_NAME'] == 'localhost' ? print("<div class='debug'>$message - $caller_name\r\n</div>") : false;
638
639
    classSupernova::log_file("$message - $caller_name");
640
    if($die) {
641
      $die && die("<div class='negative'>СТОП! Функция {$caller_name} при вызове в " . get_called_class() . " (располагается в " . get_class() . "). СООБЩИТЕ АДМИНИСТРАЦИИ!</div>");
642
    }
643
  }
644
645
}
646