- Introduced `safe_id_segment`, `safe_centro_id`, and `safe_aulario_config_path` functions to sanitize input and construct file paths securely. - Updated `index.php`, `paneldiario.php`, `proyectos.php`, `aularios.php`, `centros.php`, `club_mkthumb.php`, `reset_password.php`, and `users.php` to utilize new sanitization functions. - Enhanced error handling for file existence checks and directory traversal prevention. - Ensured consistent use of safe path handling in user input across the application.
327 lines
15 KiB
PHP
327 lines
15 KiB
PHP
<?php
|
|
require_once "_incl/auth_redir.php";
|
|
require_once "_incl/tools.security.php";
|
|
|
|
function safe_username($value)
|
|
{
|
|
$value = basename((string)$value);
|
|
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
|
|
if (strpos($value, '..') !== false) {
|
|
return '';
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
function safe_centro_id($value)
|
|
{
|
|
return preg_replace('/[^0-9]/', '', (string)$value);
|
|
}
|
|
|
|
function safe_aulario_id($value)
|
|
{
|
|
$value = basename((string)$value);
|
|
return preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
|
|
}
|
|
|
|
switch ($_GET['form'] ?? '') {
|
|
case 'save_edit':
|
|
$username = safe_username(Sf($_POST['username'] ?? ''));
|
|
if (empty($username)) {
|
|
die("Nombre de usuario no proporcionado.");
|
|
}
|
|
$user_file = "/DATA/Usuarios/$username.json";
|
|
$userdata_old = [];
|
|
if (is_readable($user_file)) {
|
|
$file_contents = file_get_contents($user_file);
|
|
if ($file_contents !== false) {
|
|
$decoded = json_decode($file_contents, true);
|
|
if (is_array($decoded)) {
|
|
$userdata_old = $decoded;
|
|
}
|
|
}
|
|
}
|
|
$permissions = $_POST['permissions'] ?? [];
|
|
if (!is_array($permissions)) {
|
|
$permissions = [];
|
|
}
|
|
|
|
$aulas = $_POST['aulas'] ?? [];
|
|
if (!is_array($aulas)) {
|
|
$aulas = [];
|
|
}
|
|
$aulas = array_values(array_filter(array_map('safe_aulario_id', $aulas)));
|
|
|
|
$userdata_new = [
|
|
'display_name' => $_POST['display_name'] ?? '',
|
|
'email' => $_POST['email'] ?? '',
|
|
'permissions' => $permissions,
|
|
'entreaulas' => [
|
|
'centro' => safe_centro_id($_POST['centro'] ?? ''),
|
|
'role' => $_POST['role'] ?? '',
|
|
'aulas' => $aulas
|
|
]
|
|
];
|
|
// Merge old and new data to preserve any other fields, like password hashes or custom metadata.
|
|
$userdata = array_merge($userdata_old, $userdata_new);
|
|
file_put_contents("/DATA/Usuarios/$username.json", json_encode($userdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
header("Location: ?action=edit&user=" . urlencode($username) . "&_result=" . urlencode("Cambios guardados correctamente a las ".date("H:i:s")." (hora servidor)."));
|
|
exit;
|
|
break;
|
|
}
|
|
|
|
switch ($_GET['action'] ?? '') {
|
|
case 'add':
|
|
require_once "_incl/pre-body.php";
|
|
?>
|
|
<form method="post" action="?form=save_edit">
|
|
<div class="card pad">
|
|
<div>
|
|
<h1 class="card-title">Agregar Nuevo Usuario</h1>
|
|
<div class="mb-3">
|
|
<label for="username" class="form-label">Nombre de usuario:</label>
|
|
<input type="text" id="username" name="username" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="display_name" class="form-label">Nombre para mostrar:</label>
|
|
<input type="text" id="display_name" name="display_name" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Correo electrónico:</label>
|
|
<input type="email" id="email" name="email" class="form-control" required>
|
|
</div>
|
|
<b>Permisos:</b>
|
|
<div class="accordion mt-3" id="permissionsAccordion">
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header" id="headingSysadmin">
|
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin" aria-expanded="true" aria-controls="collapseSysadmin">
|
|
Administración del sistema
|
|
</button>
|
|
</h2>
|
|
<div id="collapseSysadmin" class="accordion-collapse collapse show" aria-labelledby="headingSysadmin" data-bs-parent="#permissionsAccordion">
|
|
<div class="accordion-body">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access">
|
|
<label class="form-check-label" for="sysadmin-access">
|
|
Acceso
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header" id="headingEntreaulas">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas" aria-expanded="false" aria-controls="collapseEntreaulas">
|
|
EntreAulas
|
|
</button>
|
|
</h2>
|
|
<div id="collapseEntreaulas" class="accordion-collapse collapse" aria-labelledby="headingEntreaulas" data-bs-parent="#permissionsAccordion">
|
|
<div class="accordion-body">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access">
|
|
<label class="form-check-label" for="entreaulas-access">
|
|
Acceso
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente">
|
|
<label class="form-check-label" for="entreaulas-docente">
|
|
Docente
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete">
|
|
<label class="form-check-label" for="entreaulas-proyectos-delete">
|
|
Eliminar Proyectos
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary mt-3">Crear Usuario</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<?php
|
|
require_once "_incl/post-body.php";
|
|
break;
|
|
case 'edit':
|
|
require_once "_incl/pre-body.php";
|
|
$username = safe_username(Sf($_GET['user'] ?? ''));
|
|
if (empty($username)) {
|
|
die("Nombre de usuario inválido.");
|
|
}
|
|
$user_file = "/DATA/Usuarios/$username.json";
|
|
if (!file_exists($user_file) || !is_readable($user_file)) {
|
|
die("Usuario no encontrado o datos no disponibles.");
|
|
}
|
|
$userdata = json_decode(file_get_contents($user_file), true) ?? [];
|
|
?>
|
|
<form method="post" action="?form=save_edit">
|
|
<div class="card pad">
|
|
<div>
|
|
<h1>Editar Usuario: <?php echo htmlspecialchars($username); ?></h1>
|
|
<div class="mb-3">
|
|
<label for="display_name" class="form-label">Nombre para mostrar:</label>
|
|
<input type="text" id="display_name" name="display_name" value="<?php echo htmlspecialchars($userdata['display_name'] ?? ''); ?>" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Correo electrónico:</label>
|
|
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($userdata['email'] ?? ''); ?>" class="form-control" required>
|
|
</div>
|
|
<b>Permisos:</b>
|
|
<div class="accordion mt-3" id="permissionsAccordion">
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header" id="headingSysadmin">
|
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin" aria-expanded="true" aria-controls="collapseSysadmin">
|
|
Administración del sistema
|
|
</button>
|
|
</h2>
|
|
<div id="collapseSysadmin" class="accordion-collapse collapse show" aria-labelledby="headingSysadmin" data-bs-parent="#permissionsAccordion">
|
|
<div class="accordion-body">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access" <?php if (in_array('sysadmin:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
|
<label class="form-check-label" for="sysadmin-access">
|
|
Acceso
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header" id="headingEntreaulas">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas" aria-expanded="false" aria-controls="collapseEntreaulas">
|
|
EntreAulas
|
|
</button>
|
|
</h2>
|
|
<div id="collapseEntreaulas" class="accordion-collapse collapse" aria-labelledby="headingEntreaulas" data-bs-parent="#permissionsAccordion">
|
|
<div class="accordion-body">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access" <?php if (in_array('entreaulas:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
|
<label class="form-check-label" for="entreaulas-access">
|
|
Acceso
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente" <?php if (in_array('entreaulas:docente', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
|
<label class="form-check-label" for="entreaulas-docente">
|
|
Docente
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete" <?php if (in_array('entreaulas:proyectos:delete', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
|
<label class="form-check-label" for="entreaulas-proyectos-delete">
|
|
Eliminar Proyectos
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<input type="hidden" name="username" value="<?php echo htmlspecialchars($username); ?>">
|
|
<button type="submit" class="btn btn-primary mt-3">Guardar Cambios</button>
|
|
</div>
|
|
</div>
|
|
<div class="card pad">
|
|
<div>
|
|
<h2>EntreAulas: Configuración</h2>
|
|
<div class="mb-3">
|
|
<label for="centro" class="form-label">Centro asociado:</label>
|
|
<select id="centro" name="centro" class="form-select" required>
|
|
<option value="" <?php if (empty($userdata["entreaulas"]['centro'] ?? '')) echo 'selected'; ?>>-- Selecciona un centro --</option>
|
|
<?php
|
|
$centros_folders = glob("/DATA/entreaulas/Centros/*", GLOB_ONLYDIR);
|
|
foreach ($centros_folders as $centro_folder) {
|
|
$centro_id = basename($centro_folder);
|
|
echo '<option value="' . htmlspecialchars($centro_id) . '"';
|
|
if (($userdata["entreaulas"]['centro'] ?? '') === $centro_id) {
|
|
echo ' selected';
|
|
}
|
|
echo '>' . htmlspecialchars($centro_id) . '</option>';
|
|
}
|
|
?>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="role" class="form-label">Rol en EntreAulas:</label>
|
|
<select id="role" name="role" class="form-select" required>
|
|
<option value="" <?php if (empty($userdata["entreaulas"]['role'] ?? '')) echo 'selected'; ?>>-- Selecciona un rol --</option>
|
|
<option value="teacher" <?php if (($userdata["entreaulas"]['role'] ?? '') === 'teacher') echo 'selected'; ?>>Profesor</option>
|
|
<option value="student" <?php if (($userdata["entreaulas"]['role'] ?? '') === 'student') echo 'selected'; ?>>Estudiante</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Aulas asignadas: <small>(Guarda primero para actualizar la lista)</small></label><br>
|
|
<?php
|
|
$user_centro = safe_centro_id($userdata["entreaulas"]['centro'] ?? '');
|
|
$aulas_filelist = $user_centro !== '' ? (glob("/DATA/entreaulas/Centros/" . $user_centro . "/Aularios/*.json") ?: []) : [];
|
|
foreach ($aulas_filelist as $aula_file) {
|
|
$aula_data = json_decode(file_get_contents($aula_file), true);
|
|
$aula_id = safe_aulario_id(basename($aula_file, ".json"));
|
|
$is_assigned = in_array($aula_id, $userdata["entreaulas"]['aulas'] ?? []);
|
|
echo '<div class="form-check form-check-inline">';
|
|
echo '<input class="form-check-input" type="checkbox" name="aulas[]" value="' . htmlspecialchars($aula_id) . '" id="aula-' . htmlspecialchars($aula_id) . '" ' . ($is_assigned ? 'checked' : '') . '>';
|
|
echo '<label class="form-check-label" for="aula-' . htmlspecialchars($aula_id) . '">' . htmlspecialchars($aula_data['name'] ?? $aula_id) . '</label>';
|
|
echo '</div>';
|
|
}
|
|
?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card pad">
|
|
<div>
|
|
<h2>Cambiar contraseña</h2>
|
|
<p>Para cambiar la contraseña de este usuario, utiliza la herramienta de restablecimiento de contraseñas disponible en el siguiente enlace:</p>
|
|
<a href="/sysadmin/reset_password.php?user=<?php echo urlencode($username); ?>" class="btn btn-secondary">Restablecer Contraseña</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<?php
|
|
require_once "_incl/post-body.php";
|
|
break;
|
|
case "index":
|
|
default:
|
|
require_once "_incl/pre-body.php";
|
|
?>
|
|
<div class="card pad">
|
|
<div>
|
|
<h1>Gestión de Usuarios</h1>
|
|
<p>Desde esta sección puedes gestionar los usuarios del sistema. Puedes agregar, editar o eliminar usuarios según sea necesario.</p>
|
|
<table class="table table-striped table-hover">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Usuario</th>
|
|
<th>Nombre</th>
|
|
<th>Correo</th>
|
|
<th>
|
|
<a href="?action=add" class="btn btn-success">+ Nuevo</a>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php
|
|
$users_filelist = glob("/DATA/Usuarios/*.json");
|
|
foreach ($users_filelist as $user_file) {
|
|
$userdata = json_decode(file_get_contents($user_file), true);
|
|
// Username is the filename without path and extension
|
|
$username = basename($user_file, ".json");
|
|
echo "<tr>";
|
|
echo "<td>" . htmlspecialchars($username) . "</td>";
|
|
echo "<td>" . htmlspecialchars($userdata['display_name'] ?? 'N/A') . "</td>";
|
|
echo "<td>" . htmlspecialchars($userdata['email'] ?? 'N/A') . "</td>";
|
|
echo "<td>";
|
|
echo '<a href="?action=edit&user=' . urlencode($username) . '" class="btn btn-primary">Editar</a> ';
|
|
echo '<a href="?action=delete&user=' . urlencode($username) . '" class="btn btn-danger">Eliminar</a>';
|
|
echo "</td>";
|
|
echo "</tr>";
|
|
}
|
|
?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
require_once "_incl/post-body.php";
|
|
break;
|
|
}
|
|
?>
|