loreal-global-kickoff/UserRoleManager.php
Vadym Samoilenko 53e9365c01 Add Azure AD SSO, RBAC (admin/user roles), and server-setup improvements
- Enable SSO with Azure AD credentials (tenant + client ID + redirect_uri)
- Add JWTValidator.php: RS256 idToken validation via Azure JWKS with 1h cache
- Add auth.php: POST login handler sets auth cookie, GET logout clears it
- Add UserRoleManager.php: file-based role CRUD in data/user_roles.json
- Add admin.php: admin-only role management panel
- AuthMiddleware: add requireAdmin(), role in user array, fix MSAL redirect
- header.php: hide Activity Logs + Admin Panel tabs for non-admin users
- logs-viewer.php: protect with requireAdmin() instead of requireAuth()
- server-setup.sh: add composer check, data/ dir, PHP extension checks, SSO validation
- .gitignore: add data/ directory

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 20:34:50 +00:00

109 lines
2.8 KiB
PHP

<?php
/**
* User Role Manager
* File-based RBAC using data/user_roles.json
*/
class UserRoleManager {
private string $dataFile;
private string $defaultRole;
private array $adminEmails;
public function __construct() {
$config = require __DIR__ . '/config.php';
$this->dataFile = $config['roles']['data_file'];
$this->defaultRole = $config['roles']['default_role'] ?? 'user';
$this->adminEmails = array_map('strtolower', $config['roles']['admin_emails'] ?? []);
$this->ensureDataFile();
}
/**
* Get role for a user. Auto-promotes admin_emails on first lookup.
*/
public function getRole(string $email): string {
$email = strtolower(trim($email));
$roles = $this->loadRoles();
if (isset($roles[$email])) {
return $roles[$email];
}
// Auto-promote if in admin_emails list
if (in_array($email, $this->adminEmails, true)) {
$this->setRole($email, 'admin');
return 'admin';
}
return $this->defaultRole;
}
/**
* Set role for a user
*/
public function setRole(string $email, string $role): void {
$email = strtolower(trim($email));
if (!in_array($role, ['admin', 'user'], true)) {
throw new InvalidArgumentException("Invalid role: $role");
}
$roles = $this->loadRoles();
$roles[$email] = $role;
$this->saveRoles($roles);
}
/**
* Remove a user from the roles file (reverts to default role)
*/
public function removeUser(string $email): void {
$email = strtolower(trim($email));
$roles = $this->loadRoles();
unset($roles[$email]);
$this->saveRoles($roles);
}
/**
* Get all users with their roles
*/
public function getAllUsers(): array {
$roles = $this->loadRoles();
$users = [];
foreach ($roles as $email => $role) {
$users[] = ['email' => $email, 'role' => $role];
}
usort($users, fn($a, $b) => strcmp($a['email'], $b['email']));
return $users;
}
private function loadRoles(): array {
if (!file_exists($this->dataFile)) {
return [];
}
$content = file_get_contents($this->dataFile);
$data = json_decode($content, true);
return is_array($data) ? $data : [];
}
private function saveRoles(array $roles): void {
$this->ensureDataFile();
file_put_contents($this->dataFile, json_encode($roles, JSON_PRETTY_PRINT));
}
private function ensureDataFile(): void {
$dir = dirname($this->dataFile);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
if (!file_exists($this->dataFile)) {
file_put_contents($this->dataFile, '{}');
}
}
}