Initial commit
This commit is contained in:
300
api/admin/roles.php
Normal file
300
api/admin/roles.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* BOHA Automation - Roles API
|
||||
*
|
||||
* GET /api/admin/roles.php - List all roles with permissions
|
||||
* POST /api/admin/roles.php - Create new role
|
||||
* PUT /api/admin/roles.php?id=X - Update role
|
||||
* DELETE /api/admin/roles.php?id=X - Delete role
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once dirname(__DIR__) . '/config.php';
|
||||
require_once dirname(__DIR__) . '/includes/JWTAuth.php';
|
||||
require_once dirname(__DIR__) . '/includes/AuditLog.php';
|
||||
|
||||
// Set headers
|
||||
setCorsHeaders();
|
||||
setSecurityHeaders();
|
||||
setNoCacheHeaders();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Require authentication
|
||||
$authData = JWTAuth::requireAuth();
|
||||
AuditLog::setUser($authData['user_id'], $authData['user']['username'] ?? 'unknown');
|
||||
|
||||
// Require settings.roles permission
|
||||
requirePermission($authData, 'settings.roles');
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$roleId = isset($_GET['id']) ? (int) $_GET['id'] : null;
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
handleGetRole($pdo);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
handleCreateRole($pdo);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
if (!$roleId) {
|
||||
errorResponse('ID role je povinné');
|
||||
}
|
||||
handleUpdateRole($pdo, $roleId);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if (!$roleId) {
|
||||
errorResponse('ID role je povinné');
|
||||
}
|
||||
handleDeleteRole($pdo, $roleId);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorResponse('Metoda není povolena', 405);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log('Roles API error: ' . $e->getMessage());
|
||||
errorResponse('Chyba databáze', 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET - List all roles with their permissions + all available permissions
|
||||
*/
|
||||
function handleGetRole(PDO $pdo): void
|
||||
{
|
||||
// Get all roles with user count (LEFT JOIN instead of correlated subquery)
|
||||
$stmt = $pdo->query('
|
||||
SELECT r.*, COUNT(u.id) as user_count
|
||||
FROM roles r
|
||||
LEFT JOIN users u ON u.role_id = r.id
|
||||
GROUP BY r.id
|
||||
ORDER BY r.id
|
||||
');
|
||||
$roles = $stmt->fetchAll();
|
||||
|
||||
// Batch fetch all role-permission mappings in one query (was N+1)
|
||||
$stmt = $pdo->query('
|
||||
SELECT rp.role_id, p.name
|
||||
FROM role_permissions rp
|
||||
JOIN permissions p ON p.id = rp.permission_id
|
||||
');
|
||||
$allRolePerms = $stmt->fetchAll();
|
||||
|
||||
// Group permissions by role_id
|
||||
$permsByRole = [];
|
||||
foreach ($allRolePerms as $rp) {
|
||||
$permsByRole[$rp['role_id']][] = $rp['name'];
|
||||
}
|
||||
|
||||
foreach ($roles as &$role) {
|
||||
$role['permissions'] = $permsByRole[$role['id']] ?? [];
|
||||
$role['permission_count'] = count($role['permissions']);
|
||||
}
|
||||
unset($role);
|
||||
|
||||
// Get all available permissions grouped by module
|
||||
$stmt = $pdo->query('SELECT id, name, display_name, description FROM permissions ORDER BY id');
|
||||
$allPermissions = $stmt->fetchAll();
|
||||
|
||||
$grouped = [];
|
||||
foreach ($allPermissions as $perm) {
|
||||
$parts = explode('.', $perm['name'], 2);
|
||||
$module = $parts[0];
|
||||
if (!isset($grouped[$module])) {
|
||||
$grouped[$module] = [];
|
||||
}
|
||||
$grouped[$module][] = $perm;
|
||||
}
|
||||
|
||||
successResponse([
|
||||
'roles' => $roles,
|
||||
'permissions' => $allPermissions,
|
||||
'permission_groups' => $grouped,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST - Create new role
|
||||
*/
|
||||
function handleCreateRole(PDO $pdo): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
|
||||
$name = trim($input['name'] ?? '');
|
||||
$displayName = trim($input['display_name'] ?? '');
|
||||
$description = trim($input['description'] ?? '');
|
||||
$permissions = $input['permissions'] ?? [];
|
||||
|
||||
if (!$name) {
|
||||
errorResponse('Název role je povinný');
|
||||
}
|
||||
|
||||
if (!$displayName) {
|
||||
errorResponse('Zobrazovaný název je povinný');
|
||||
}
|
||||
|
||||
// Validate name format (slug)
|
||||
if (!preg_match('/^[a-z0-9_-]+$/', $name)) {
|
||||
errorResponse('Název role může obsahovat pouze malá písmena, čísla, pomlčky a podtržítka');
|
||||
}
|
||||
|
||||
// Check uniqueness
|
||||
$stmt = $pdo->prepare('SELECT id FROM roles WHERE name = ?');
|
||||
$stmt->execute([$name]);
|
||||
if ($stmt->fetch()) {
|
||||
errorResponse('Role s tímto názvem již existuje');
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// Create role
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO roles (name, display_name, description)
|
||||
VALUES (?, ?, ?)
|
||||
');
|
||||
$stmt->execute([$name, $displayName, $description ?: null]);
|
||||
$newRoleId = (int)$pdo->lastInsertId();
|
||||
|
||||
// Assign permissions
|
||||
if (!empty($permissions)) {
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT ?, id FROM permissions WHERE name = ?
|
||||
');
|
||||
foreach ($permissions as $permName) {
|
||||
$stmt->execute([$newRoleId, $permName]);
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
AuditLog::logCreate('role', $newRoleId, [
|
||||
'name' => $name,
|
||||
'display_name' => $displayName,
|
||||
'permissions' => $permissions,
|
||||
], "Vytvořena role '$displayName'");
|
||||
|
||||
successResponse(['id' => $newRoleId], 'Role byla vytvořena');
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT - Update role
|
||||
*/
|
||||
function handleUpdateRole(PDO $pdo, int $roleId): void
|
||||
{
|
||||
// Get existing role
|
||||
$stmt = $pdo->prepare('SELECT * FROM roles WHERE id = ?');
|
||||
$stmt->execute([$roleId]);
|
||||
$role = $stmt->fetch();
|
||||
|
||||
if (!$role) {
|
||||
errorResponse('Role nebyla nalezena', 404);
|
||||
}
|
||||
|
||||
// Block editing admin role name
|
||||
if ($role['name'] === 'admin') {
|
||||
errorResponse('Roli administrátora nelze upravovat');
|
||||
}
|
||||
|
||||
$input = getJsonInput();
|
||||
|
||||
$displayName = trim($input['display_name'] ?? $role['display_name']);
|
||||
$description = trim($input['description'] ?? $role['description'] ?? '');
|
||||
$permissions = $input['permissions'] ?? null;
|
||||
|
||||
if (!$displayName) {
|
||||
errorResponse('Zobrazovaný název je povinný');
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// Update role
|
||||
$stmt = $pdo->prepare('
|
||||
UPDATE roles SET display_name = ?, description = ?
|
||||
WHERE id = ?
|
||||
');
|
||||
$stmt->execute([$displayName, $description ?: null, $roleId]);
|
||||
|
||||
// Update permissions if provided
|
||||
if ($permissions !== null) {
|
||||
// Remove existing permissions
|
||||
$stmt = $pdo->prepare('DELETE FROM role_permissions WHERE role_id = ?');
|
||||
$stmt->execute([$roleId]);
|
||||
|
||||
// Add new permissions
|
||||
if (!empty($permissions)) {
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT ?, id FROM permissions WHERE name = ?
|
||||
');
|
||||
foreach ($permissions as $permName) {
|
||||
$stmt->execute([$roleId, $permName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
AuditLog::logUpdate('role', $roleId, [
|
||||
'display_name' => $role['display_name'],
|
||||
], [
|
||||
'display_name' => $displayName,
|
||||
'permissions' => $permissions,
|
||||
], "Upravena role '$displayName'");
|
||||
|
||||
successResponse(null, 'Role byla aktualizována');
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE - Delete role
|
||||
*/
|
||||
function handleDeleteRole(PDO $pdo, int $roleId): void
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT * FROM roles WHERE id = ?');
|
||||
$stmt->execute([$roleId]);
|
||||
$role = $stmt->fetch();
|
||||
|
||||
if (!$role) {
|
||||
errorResponse('Role nebyla nalezena', 404);
|
||||
}
|
||||
|
||||
// Block deleting admin role
|
||||
if ($role['name'] === 'admin') {
|
||||
errorResponse('Roli administrátora nelze smazat');
|
||||
}
|
||||
|
||||
// Check if role has users
|
||||
$stmt = $pdo->prepare('SELECT COUNT(*) FROM users WHERE role_id = ?');
|
||||
$stmt->execute([$roleId]);
|
||||
$userCount = $stmt->fetchColumn();
|
||||
|
||||
if ($userCount > 0) {
|
||||
errorResponse("Nelze smazat roli s {$userCount} přiřazenými uživateli. Nejprve změňte roli těmto uživatelům.");
|
||||
}
|
||||
|
||||
// Delete role (cascade deletes role_permissions)
|
||||
$stmt = $pdo->prepare('DELETE FROM roles WHERE id = ?');
|
||||
$stmt->execute([$roleId]);
|
||||
|
||||
AuditLog::logDelete('role', $roleId, $role, "Smazána role '{$role['display_name']}'");
|
||||
|
||||
successResponse(null, 'Role byla smazána');
|
||||
}
|
||||
Reference in New Issue
Block a user