« Back to History
auth.php
|
20260721_154033.php
Initial Bulk Import
Copy Code
<?php /* ============================================================================ File: /erp/modules/auth/auth.php (SAFE ROLLBACK) Purpose : Session auth helpers (users table only) Login : identifier = email OR username OR mobile OR name Hashes : bcrypt/argon2 ($2y$/$2b$), MD5, SHA256, plain (fallback + optional upgrade) Debug : add ?auth_dbg=1 while submitting login form ============================================================================ */ declare(strict_types=1); error_reporting(E_ALL); ini_set('display_errors','1'); /* ---- Include Guard ---- */ if (defined('MM_AUTH_LOADED')) return; define('MM_AUTH_LOADED', true); /* ---- Sessions ---- */ $__sess_dir = dirname(__DIR__, 2) . '/tmp_sessions'; if (!is_dir($__sess_dir)) { @mkdir($__sess_dir, 0775, true); } @ini_set('session.save_path', $__sess_dir); if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } /* ---- DB ---- */ require_once dirname(__DIR__, 2) . '/core/db.php'; $pdo = $GLOBALS['pdo'] ?? null; /* ---- Helpers ------------------------------------------------------------ */ if (!function_exists('users_table_columns')) { // compat for old code function users_table_columns(): array { return ['id','company_id','name','email','password_hash','role','created_at']; } } if (!function_exists('mm_sw')) { function mm_sw(string $s, string $p): bool { return strncmp($s,$p,strlen($p)) === 0; } } if (!function_exists('mm_pw_verify')) { function mm_pw_verify(string $input, string $stored): bool { $s = trim((string)$stored); if ($s === '') return false; // bcrypt family if (mm_sw($s,'$2y$') || mm_sw($s,'$2a$') || mm_sw($s,'$2x$')) return password_verify($input,$s); if (mm_sw($s,'$2b$')) { $s2 = '$2y$' . substr($s,4); if (password_verify($input,$s2)) return true; if (password_verify($input,$s)) return true; return false; } // argon2 if (mm_sw($s,'$argon2')) return password_verify($input,$s); // legacy if (preg_match('/^[a-f0-9]{32}$/i',$s)) return strtolower(md5($input)) === strtolower($s); // MD5 if (preg_match('/^[a-f0-9]{64}$/i',$s)) return hash('sha256',$input) === strtolower($s); // SHA256 // plain (last resort) return hash_equals($s,$input); } } if (!function_exists('mm_upgrade_password')) { function mm_upgrade_password(PDO $pdo, int $id, string $newPlain): void { try { $hash = password_hash($newPlain, PASSWORD_BCRYPT); $st = $pdo->prepare("UPDATE `users` SET `password_hash`=:h, `password_plain`=NULL WHERE `id`=:id LIMIT 1"); $st->execute([':h'=>$hash, ':id'=>$id]); } catch (Throwable $e) { /* ignore */ } } } /* ---- Public API --------------------------------------------------------- */ if (!function_exists('auth_user')) { function auth_user(): ?array { return (isset($_SESSION['user']) && is_array($_SESSION['user'])) ? $_SESSION['user'] : null; } } /** users table only */ if (!function_exists('auth_fetch_user')) { function auth_fetch_user(PDO $pdo, string $identifier): ?array { $id = trim($identifier); $sql = "SELECT id, company_id, name, email, role, username, mobile, password_hash, password_plain FROM users WHERE email=:id OR username=:id OR mobile=:id OR name=:id LIMIT 1"; $st = $pdo->prepare($sql); $st->execute([':id'=>$id]); $r = $st->fetch(PDO::FETCH_ASSOC); return $r ?: null; } } if (!function_exists('auth_login_any')) { function auth_login_any(string $identifier, string $password): bool { global $pdo; if (!$pdo) return false; $dbg = !empty($_GET['auth_dbg']); $row = auth_fetch_user($pdo, $identifier); if (!$row) { if ($dbg) { header('Content-Type:text/plain'); echo "NO ROW in users for [$identifier]\n"; exit; } return false; } $ok = false; if (!empty($row['password_hash'])) { $ok = mm_pw_verify($password, (string)$row['password_hash']); } // plain fallback + upgrade if (!$ok && !empty($row['password_plain']) && hash_equals((string)$row['password_plain'], $password)) { $ok = true; mm_upgrade_password($pdo, (int)$row['id'], $password); } if ($dbg) { header('Content-Type:text/plain'); echo "ROW FOUND: id={$row['id']} company_id={$row['company_id']} username={$row['username']} email={$row['email']}\n"; echo "HASH? ".(!empty($row['password_hash'])?'YES':'NO')." PLAIN? ".(!empty($row['password_plain'])?'YES':'NO')."\n"; echo "PASSWORD_MATCH: ".($ok?'YES':'NO')."\n"; exit; } if (!$ok) return false; session_regenerate_id(true); $_SESSION['user'] = [ 'id' => (int)$row['id'], 'company_id' => (int)$row['company_id'], 'name' => (string)$row['name'], 'email' => (string)($row['email'] ?? ''), 'role' => (string)($row['role'] ?? ''), ]; return true; } } /* Back-compat wrappers */ if (!function_exists('auth_login')) { function auth_login(string $emailOrUsername, string $password): bool { return auth_login_any($emailOrUsername,$password); } } if (!function_exists('auth_login_mobile')) { function auth_login_mobile(string $mobile, string $password): bool { return auth_login_any($mobile,$password); } } if (!function_exists('auth_login_username')) { function auth_login_username(string $username, string $password): bool { return auth_login_any($username,$password); } } /* Logout */ if (!function_exists('auth_logout')) { function auth_logout(): void { if (session_status() !== PHP_SESSION_ACTIVE) { @session_start(); } $_SESSION = []; if (ini_get("session.use_cookies")) { $p = session_get_cookie_params(); setcookie(session_name(), '', time()-42000, $p["path"], $p["domain"], $p["secure"], $p["httponly"]); } session_destroy(); } } /* Guard */ if (!function_exists('require_login')) { function require_login($roles = null): void { $u = auth_user(); if (!$u) { $next = $_SERVER['REQUEST_URI'] ?? '/erp/public/index.php'; header('Location: /erp/public/login.php?next=' . rawurlencode($next)); exit; } if ($roles === null || $roles === '') return; $role = strtolower((string)($u['role'] ?? '')); $need = is_array($roles)?$roles:[$roles]; $need_ci = array_map(fn($r)=>strtolower((string)$r), $need); if (!in_array($role, $need_ci, true)) { http_response_code(403); exit('Forbidden: role not allowed.'); } } }