Files
kaffeekasse-saas/app/Core/Auth.php
T
2026-06-15 18:36:57 +02:00

148 lines
4.3 KiB
PHP

<?php
namespace App\Core;
use PDO;
final class Auth
{
public function __construct(
private readonly PDO $pdo,
private readonly Session $session
) {
}
public function attemptPlatformLogin(string $email, string $password): bool
{
$statement = $this->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->rehashPasswordIfNeeded((int) $user['id'], (string) $user['password_hash'], $password);
$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,
member.id AS member_id
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"
LEFT JOIN members member
ON member.user_id = u.id
AND member.tenant_id = m.tenant_id
AND member.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->rehashPasswordIfNeeded((int) $membership['id'], (string) $membership['password_hash'], $password);
$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'],
'member_id' => isset($membership['member_id']) ? (int) $membership['member_id'] : null,
'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 tenantMemberId(): ?int
{
$memberId = $this->user()['member_id'] ?? null;
return is_int($memberId) ? $memberId : null;
}
public function canManageTenant(): bool
{
$role = $this->user()['tenant_role'] ?? null;
return is_string($role) && in_array($role, ['owner', 'admin'], true);
}
public function logout(): void
{
$this->session->invalidate();
}
private function rehashPasswordIfNeeded(int $userId, string $currentHash, string $password): void
{
if (!password_needs_rehash($currentHash, defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_BCRYPT)) {
return;
}
$statement = $this->pdo->prepare(
'UPDATE users SET password_hash = :password_hash WHERE id = :id'
);
$statement->execute([
'password_hash' => secure_password_hash($password),
'id' => $userId,
]);
}
}