pdo->prepare( 'SELECT id, password_hash, platform_role FROM users WHERE email = :email LIMIT 1' ); $statement->execute(['email' => mb_strtolower($email)]); $user = $statement->fetch(); if (!$user || !password_verify($password, $user['password_hash'])) { return false; } if (($user['platform_role'] ?? 'user') !== 'platform_admin') { return false; } $this->session->regenerate(); $this->session->put('auth', [ 'user_id' => (int) $user['id'], 'platform_role' => $user['platform_role'], ]); return true; } public function attemptTenantLogin(string $tenantSlug, string $email, string $password): bool { $statement = $this->pdo->prepare( 'SELECT u.id, u.password_hash, m.tenant_id, m.role, t.slug AS tenant_slug FROM users u INNER JOIN tenant_memberships m ON m.user_id = u.id AND m.status = "active" INNER JOIN tenants t ON t.id = m.tenant_id AND t.slug = :slug AND t.status = "active" WHERE u.email = :email LIMIT 1' ); $statement->execute([ 'slug' => $tenantSlug, 'email' => mb_strtolower($email), ]); $membership = $statement->fetch(); if (!$membership || !password_verify($password, $membership['password_hash'])) { return false; } $this->session->regenerate(); $this->session->put('auth', [ 'user_id' => (int) $membership['id'], 'tenant_id' => (int) $membership['tenant_id'], 'tenant_slug' => $membership['tenant_slug'], 'tenant_role' => $membership['role'], 'platform_role' => 'user', ]); return true; } public function user(): ?array { $auth = $this->session->get('auth'); return is_array($auth) ? $auth : null; } public function checkPlatformAdmin(): bool { return ($this->user()['platform_role'] ?? null) === 'platform_admin'; } public function checkTenantAccess(string $tenantSlug): bool { $auth = $this->user(); return is_array($auth) && isset($auth['tenant_slug']) && hash_equals($auth['tenant_slug'], $tenantSlug); } public function requireRole(array $allowedRoles): bool { $role = $this->user()['tenant_role'] ?? null; return is_string($role) && in_array($role, $allowedRoles, true); } public function logout(): void { $this->session->invalidate(); } }