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; } }