307 lines
8.3 KiB
PHP
307 lines
8.3 KiB
PHP
<?php
|
||
|
||
namespace admin\factory;
|
||
|
||
class Users
|
||
{
|
||
public static function user_delete($user_id)
|
||
{
|
||
global $mdb;
|
||
|
||
return $mdb->delete('pp_users', ['id' => (int)$user_id]);
|
||
}
|
||
|
||
public static function user_details($user_id)
|
||
{
|
||
global $mdb;
|
||
return $mdb->get('pp_users', '*', ['id' => (int)$user_id]);
|
||
}
|
||
|
||
public static function user_privileges($user_id)
|
||
{
|
||
global $mdb;
|
||
return $mdb->select('pp_users_privileges', '*', ['id_user' => (int)$user_id]);
|
||
}
|
||
|
||
public static function user_save($user_id, $login, $status, $active_to, $password, $password_re, $admin, $privileges, $twofa_enabled = 0, $twofa_email = '' )
|
||
{
|
||
global $mdb, $lang;
|
||
|
||
$mdb->delete('pp_users_privileges', ['id_user' => (int) $user_id]);
|
||
|
||
if (!$user_id)
|
||
{
|
||
if (strlen($password) < 5)
|
||
return $response = ['status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.'];
|
||
|
||
if ($password != $password_re)
|
||
return $response = ['status' => 'error', 'msg' => 'Podane hasła są różne'];
|
||
|
||
if ($mdb->insert(
|
||
'pp_users',
|
||
[
|
||
'login' => $login,
|
||
'status' => $status == 'on' ? 1 : 0,
|
||
'active_to' => $active_to == '' ? NULL : $active_to,
|
||
'admin' => $admin,
|
||
'password' => md5($password),
|
||
'twofa_enabled' => $twofa_enabled == 'on' ? 1 : 0,
|
||
'twofa_email' => $twofa_email
|
||
]
|
||
))
|
||
$id_user = $mdb->get('pp_users', 'id', ['ORDER' => ['id' => 'DESC']]);
|
||
|
||
if (is_array($privileges))
|
||
{
|
||
foreach ($privileges as $pri)
|
||
{
|
||
$mdb->insert(
|
||
'pp_users_privileges',
|
||
[
|
||
'name' => $pri,
|
||
'id_user' => $id_user
|
||
]
|
||
);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
$mdb->insert(
|
||
'pp_users_privileges',
|
||
[
|
||
'name' => $privileges,
|
||
'id_user' => $id_user
|
||
]
|
||
);
|
||
}
|
||
|
||
return $response = ['status' => 'ok', 'msg' => 'Użytkownik został zapisany.'];
|
||
}
|
||
else
|
||
{
|
||
|
||
if ($password and strlen($password) < 5)
|
||
return $response = ['status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.'];
|
||
|
||
if ($password and $password != $password_re)
|
||
return $response = ['status' => 'error', 'msg' => 'Podane hasła są różne'];
|
||
|
||
if ($password)
|
||
$mdb->update('pp_users', [
|
||
'password' => md5($password)
|
||
], [
|
||
'id' => (int) $user_id
|
||
]);
|
||
|
||
$mdb->update('pp_users', [
|
||
'login' => $login,
|
||
'admin' => $admin,
|
||
'status' => $status == 'on' ? 1 : 0,
|
||
'active_to' => $active_to == '' ? NULL : $active_to,
|
||
'error_logged_count' => 0,
|
||
'twofa_enabled' => $twofa_enabled == 'on' ? 1 : 0,
|
||
'twofa_email' => $twofa_email
|
||
], [
|
||
'id' => (int) $user_id
|
||
]);
|
||
|
||
if (is_array($privileges))
|
||
{
|
||
foreach ($privileges as $pri)
|
||
{
|
||
$mdb->insert('pp_users_privileges', [
|
||
'name' => $pri,
|
||
'id_user' => $user_id
|
||
]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
$mdb->insert('pp_users_privileges', [
|
||
'name' => $privileges,
|
||
'id_user' => $user_id
|
||
]);
|
||
}
|
||
return $response = ['status' => 'ok', 'msg' => 'Uzytkownik został zapisany.'];
|
||
}
|
||
\S::delete_cache();
|
||
}
|
||
|
||
public static function check_login($login, $user_id)
|
||
{
|
||
global $mdb;
|
||
|
||
if ($mdb->get('pp_users', 'login', ['AND' => ['login' => $login, 'id[!]' => (int)$user_id]]))
|
||
return $response = ['status' => 'error', 'msg' => 'Podany login jest już zajęty.'];
|
||
|
||
return $response = ['status' => 'ok'];
|
||
}
|
||
|
||
public static function logon($login, $password)
|
||
{
|
||
global $mdb;
|
||
|
||
if (!$mdb->get('pp_users', '*', ['login' => $login]))
|
||
return 0;
|
||
|
||
if (!$mdb->get('pp_users', '*', ['AND' => ['login' => $login, 'status' => 1, 'error_logged_count[<]' => 5]]))
|
||
return -1;
|
||
|
||
if ($mdb->get('pp_users', '*', [
|
||
'AND' => [
|
||
'login' => $login,
|
||
'status' => 1,
|
||
'password' => md5($password),
|
||
'OR' => ['active_to[>=]' => date('Y-m-d'), 'active_to' => null]
|
||
]
|
||
]))
|
||
{
|
||
$mdb->update('pp_users', ['last_logged' => date('Y-m-d H:i:s'), 'error_logged_count' => 0], ['login' => $login]);
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
$mdb->update('pp_users', ['last_error_logged' => date('Y-m-d H:i:s'), 'error_logged_count[+]' => 1], ['login' => $login]);
|
||
if ($mdb->get('pp_users', 'error_logged_count', ['login' => $login]) >= 5)
|
||
{
|
||
$mdb->update('pp_users', ['status' => 0], ['login' => $login]);
|
||
return -1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
public static function details($login)
|
||
{
|
||
global $mdb;
|
||
return $mdb->get('pp_users', '*', ['login' => $login]);
|
||
}
|
||
|
||
public static function check_privileges($name, $user_id)
|
||
{
|
||
global $mdb;
|
||
|
||
if ($user_id == 1)
|
||
return true;
|
||
else
|
||
{
|
||
if (!$privilages = \Cache::fetch("check_privileges:$user_id:$name-tmp"))
|
||
{
|
||
$privilages = $mdb->count('pp_users_privileges', ['AND' => ['name' => $name, 'id_user' => (int)$user_id]]);
|
||
\Cache::store("check_privileges:$user_id:$name", $privilages);
|
||
}
|
||
return $privilages;
|
||
}
|
||
}
|
||
|
||
static public function get_by_id(int $userId): ?array
|
||
{
|
||
|
||
global $mdb;
|
||
return $mdb->get('pp_users', '*', ['id' => $userId]) ?: null;
|
||
}
|
||
|
||
static public function send_twofa_code(int $userId, bool $resend = false): bool
|
||
{
|
||
|
||
$user = self::get_by_id($userId);
|
||
if (!$user)
|
||
return false;
|
||
|
||
if ((int)$user['twofa_enabled'] !== 1)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
$to = $user['twofa_email'] ?: $user['login'];
|
||
if (!filter_var($to, FILTER_VALIDATE_EMAIL))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if ($resend && !empty($user['twofa_sent_at']))
|
||
{
|
||
$last = strtotime($user['twofa_sent_at']);
|
||
if ($last && (time() - $last) < 30)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
$code = random_int(100000, 999999);
|
||
$hash = password_hash((string)$code, PASSWORD_DEFAULT);
|
||
|
||
self::update_by_id($userId, [
|
||
'twofa_code_hash' => $hash,
|
||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 10 * 60), // 10 minut
|
||
'twofa_sent_at' => date('Y-m-d H:i:s'),
|
||
'twofa_failed_attempts' => 0,
|
||
]);
|
||
|
||
$subject = 'Twój kod logowania 2FA';
|
||
$body = "Twój kod logowania do panelu administratora: {$code}. Kod jest ważny przez 10 minut. Jeśli to nie Ty inicjowałeś logowanie – zignoruj tę wiadomość i poinformuj administratora.";
|
||
|
||
$sent = \S::send_email($to, $subject, $body);
|
||
|
||
if (!$sent) {
|
||
$headers = "MIME-Version: 1.0\r\n";
|
||
$headers .= "Content-type: text/plain; charset=UTF-8\r\n";
|
||
$headers .= "From: no-reply@" . ($_SERVER['HTTP_HOST'] ?? 'localhost') . "\r\n";
|
||
$encodedSubject = mb_encode_mimeheader($subject, 'UTF-8');
|
||
|
||
$sent = mail($to, $encodedSubject, $body, $headers);
|
||
}
|
||
|
||
return $sent;
|
||
}
|
||
|
||
static public function update_by_id(int $userId, array $data): bool
|
||
{
|
||
global $mdb;
|
||
return (bool)$mdb->update('pp_users', $data, ['id' => $userId]);
|
||
}
|
||
|
||
static public function verify_twofa_code(int $userId, string $code): bool
|
||
{
|
||
$user = self::get_by_id( $userId );
|
||
if (!$user) return false;
|
||
|
||
if ((int)$user['twofa_failed_attempts'] >= 5)
|
||
{
|
||
return false; // zbyt wiele prób
|
||
}
|
||
|
||
// sprawdź ważność
|
||
if (empty($user['twofa_expires_at']) || time() > strtotime($user['twofa_expires_at']))
|
||
{
|
||
// wyczyść po wygaśnięciu
|
||
self::update_by_id($userId, [
|
||
'twofa_code_hash' => null,
|
||
'twofa_expires_at' => null,
|
||
]);
|
||
return false;
|
||
}
|
||
|
||
$ok = (!empty($user['twofa_code_hash']) && password_verify($code, $user['twofa_code_hash']));
|
||
if ($ok)
|
||
{
|
||
// sukces: czyścimy wszystko
|
||
self::update_by_id($userId, [
|
||
'twofa_code_hash' => null,
|
||
'twofa_expires_at' => null,
|
||
'twofa_sent_at' => null,
|
||
'twofa_failed_attempts' => 0,
|
||
'last_logged' => date('Y-m-d H:i:s'),
|
||
]);
|
||
return true;
|
||
}
|
||
|
||
// zła próba — inkrementacja
|
||
self::update_by_id($userId, [
|
||
'twofa_failed_attempts' => (int)$user['twofa_failed_attempts'] + 1,
|
||
'last_error_logged' => date('Y-m-d H:i:s'),
|
||
]);
|
||
return false;
|
||
}
|
||
}
|