loreal-global-kickoff/admin.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

327 lines
8.6 KiB
PHP

<?php
/**
* Admin Panel — User Role Management
* Only accessible to users with the 'admin' role
*/
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/AuthMiddleware.php';
require_once __DIR__ . '/UserRoleManager.php';
$auth = new AuthMiddleware();
$user = $auth->requireAdmin();
$roleManager = new UserRoleManager();
$pageTitle = 'Admin Panel - L\'Oréal OMG';
$message = '';
$messageType = '';
// Handle POST — update role
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$targetEmail = trim($_POST['email'] ?? '');
$targetRole = trim($_POST['role'] ?? '');
if ($targetEmail && in_array($targetRole, ['admin', 'user'], true)) {
// Prevent admin from demoting themselves
if (strtolower($targetEmail) === strtolower($user['email']) && $targetRole !== 'admin') {
$message = 'You cannot remove your own admin role.';
$messageType = 'error';
} else {
$roleManager->setRole($targetEmail, $targetRole);
$message = "Role for " . htmlspecialchars($targetEmail) . " updated to " . htmlspecialchars($targetRole) . ".";
$messageType = 'success';
}
} else {
$message = 'Invalid email or role.';
$messageType = 'error';
}
}
$allUsers = $roleManager->getAllUsers();
require_once __DIR__ . '/header.php';
?>
<div class="admin-container">
<div class="admin-section">
<h2>Admin Panel — User Role Management</h2>
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType; ?>"><?php echo $message; ?></div>
<?php endif; ?>
<!-- Add / Update Role Form -->
<div class="admin-card">
<h3>Set User Role</h3>
<form method="POST" class="role-form">
<div class="form-row">
<div class="form-group">
<label for="emailInput">User Email</label>
<input type="email" id="emailInput" name="email" required
placeholder="user@oliver.agency" class="form-input"
list="existingEmails">
<datalist id="existingEmails">
<?php foreach ($allUsers as $u): ?>
<option value="<?php echo htmlspecialchars($u['email']); ?>">
<?php endforeach; ?>
</datalist>
</div>
<div class="form-group">
<label for="roleSelect">Role</label>
<select id="roleSelect" name="role" class="form-select">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
<div class="form-group form-group--action">
<button type="submit" class="action-btn save-btn">Save Role</button>
</div>
</div>
</form>
</div>
<!-- Users Table -->
<div class="admin-card">
<h3>All Users (<?php echo count($allUsers); ?>)</h3>
<?php if (empty($allUsers)): ?>
<p class="empty-state">No users have been assigned roles yet. Roles are created automatically on first login.</p>
<?php else: ?>
<div class="users-table-wrapper">
<table class="users-table">
<thead>
<tr>
<th>Email</th>
<th>Role</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($allUsers as $u): ?>
<tr>
<td><?php echo htmlspecialchars($u['email']); ?></td>
<td>
<span class="role-pill role-<?php echo $u['role']; ?>">
<?php echo ucfirst($u['role']); ?>
</span>
</td>
<td>
<form method="POST" style="display:inline;">
<input type="hidden" name="email" value="<?php echo htmlspecialchars($u['email']); ?>">
<select name="role" class="inline-select">
<option value="user" <?php echo $u['role'] === 'user' ? 'selected' : ''; ?>>User</option>
<option value="admin" <?php echo $u['role'] === 'admin' ? 'selected' : ''; ?>>Admin</option>
</select>
<button type="submit" class="action-btn small-btn">Update</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<style>
.admin-container {
max-width: 900px;
margin: 0 auto;
}
.admin-section {
padding: 0 20px;
}
.admin-section h2 {
color: #000;
margin-bottom: 24px;
font-size: 24px;
font-weight: 700;
}
.admin-card {
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(255, 196, 7, 0.3);
padding: 30px;
margin-bottom: 24px;
}
.admin-card h3 {
font-size: 18px;
font-weight: 700;
margin-bottom: 20px;
color: #000;
border-bottom: 2px solid #FFC407;
padding-bottom: 10px;
}
.alert {
padding: 14px 20px;
border-radius: 6px;
margin-bottom: 20px;
font-weight: 600;
font-size: 14px;
}
.alert-success {
background: #e8f5e9;
color: #2e7d32;
border-left: 4px solid #4CAF50;
}
.alert-error {
background: #ffebee;
color: #c62828;
border-left: 4px solid #ff4444;
}
.role-form .form-row {
display: flex;
gap: 20px;
align-items: flex-end;
flex-wrap: wrap;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
font-size: 13px;
color: #333;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.form-input, .form-select {
padding: 10px 14px;
border: 2px solid #ddd;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
min-width: 240px;
transition: border-color 0.2s;
}
.form-input:focus, .form-select:focus {
outline: none;
border-color: #FFC407;
}
.form-group--action {
justify-content: flex-end;
}
.save-btn {
padding: 10px 24px;
background: #FFC407;
color: #000;
border: none;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.save-btn:hover {
background: #000;
color: #FFC407;
}
.users-table-wrapper {
overflow-x: auto;
}
.users-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
.users-table th {
background: #000;
color: #FFC407;
padding: 12px 16px;
text-align: left;
font-weight: 700;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.users-table td {
padding: 12px 16px;
border-bottom: 1px solid #eee;
vertical-align: middle;
}
.users-table tr:hover td {
background: rgba(255, 196, 7, 0.05);
}
.role-pill {
display: inline-block;
padding: 3px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
}
.role-pill.role-admin {
background: #FFC407;
color: #000;
}
.role-pill.role-user {
background: #f0f0f0;
color: #555;
}
.inline-select {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
margin-right: 8px;
}
.small-btn {
padding: 6px 14px;
background: #FFC407;
color: #000;
border: none;
border-radius: 4px;
font-family: 'Montserrat', sans-serif;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
}
.small-btn:hover {
background: #000;
color: #FFC407;
}
.empty-state {
color: #777;
font-size: 14px;
text-align: center;
padding: 20px;
}
</style>
</body>
</html>