Esta página contiene comentarios ampliados con fragmentos de código PHP de los ejercicios Base de datos (3) Identificación de usuarios.
En todo estos ejercicios, las páginas de gestión de la tabla deben mostrarse únicamente a los usuarios identificados. En este primer ejercicio, la identificación es automática, no se necesita indicar ningún nombre de usuario ni contraseña.
session_name($cfg["sessionName"]);
session_start();
$_SESSION["conectado"] = true;
header("location:../index.php");
session_name($cfg["sessionName"]);
session_start();
if (!isset($_SESSION["conectado"])) {
header("Location:../index.php");
exit;
}
session_name($cfg["sessionName"]);
session_start();
session_destroy();
header("location:../index.php");
// Nombre de sesión
$cfg["sessionName"] = "mclibre-bases-de-datos-3-1"; // Nombre de sesión
// Constantes y variables configurables por el programador de la aplicación
define("MENU_PRINCIPAL", 1); // Menú principal sin conectar
define("MENU_VOLVER", 2); // Menú Volver a inicio
define("MENU_PERSONAS", 3); // Menú Personas
define("PROFUNDIDAD_0", ""); // Profundidad de nivel de la página: directorio raíz
define("PROFUNDIDAD_1", "../"); // Profundidad de nivel de la página: subdirectorio
function cabecera($texto, $menu, $profundidadDirectorio)
{
...
print " <link rel=\"stylesheet\" href=\"{$profundidadDirectorio}comunes/mclibre-php-proyectos.css\" title=\"Color\" />\n";
...
cabecera("Borrar 1", MENU_PERSONAS, PROFUNDIDAD_1);
cabecera("Inicio", MENU_PRINCIPAL, PROFUNDIDAD_0);
[biblioteca.php] En un caso el menú permite gestionar la tabla personas y en el otro no:
if (!isset($_SESSION["conectado"])) {
if ($menu == MENU_PRINCIPAL) {
print " <li><a href=\"acceso/login.php\">Conectarse</a></li>\n";
} else {
print " <li>Error en la selección de menú</li>\n";
}
} else {
if ($menu == MENU_PRINCIPAL) {
print " <li><a href=\"tabla-personas/index.php\">Personas</a></li>\n";
print " <li><a href=\"acceso/logout.php\">Desconectarse</a></li>\n";
} elseif ($menu == MENU_VOLVER) {
print " <li><a href=\"../index.php\">Volver</a></li>\n";
} elseif ($menu == MENU_PERSONAS) {
print " <li><a href=\"../index.php\">Volver</a></li>\n";
print " <li><a href=\"insertar-1.php\">Añadir registro</a></li>\n";
print " <li><a href=\"listar.php\">Listar</a></li>\n";
print " <li><a href=\"borrar-1.php\">Borrar</a></li>\n";
print " <li><a href=\"buscar-1.php\">Buscar</a></li>\n";
print " <li><a href=\"modificar-1.php\">Modificar</a></li>\n";
print " <li><a href=\"borrar-todo-1.php\">Borrar todo</a></li>\n";
} else {
print " <li>Error en la selección de menú</li>\n";
}
}
cabecera("Inicio", MENU_PERSONAS, PROFUNDIDAD_1);
En este ejercicio, el usuario debe identificarse indicando su nombre y contraseña, pero sólo existe un usuario, con nombre y contraseña fijos. La contraseña de este usuario no se guarda directamente, lo que se guarda es un hash de la contraseña.
// Usuario Administrador de la aplicación
$cfg["rootName"] = "root"; // Nombre del Usuario Administrador de la aplicación
$cfg["rootPassword"] = "4813494d137e1631bba301d5acab6e7bb7aa74ce1185d456565ef51d737677b2"; // Contraseña encriptada del Usuario Administrador de la aplicación
function encripta($cadena)
{
global $cfg;
return hash($cfg["hashAlgorithm"], $cadena);
}
// Algoritmo hash para encriptar la contraseña de usuario
$cfg["hashAlgorithm"] = "sha256"; // Algoritmo hash para encriptar la contraseña de usuario
// Los posibles algoritmos son https://www.php.net/manual/en/function.hash-algos.php
En este ejercicio el login se divide en dos páginas. La primera es un formulario que pide el nombre de usuario y la contraseña. La segunda página comprueba si los datos introducidos coinciden con los del fichero de configuración.
$usuario = recoge("usuario");
$password = recoge("password");
if ($usuario != $cfg["rootName"] || encripta($password) != $cfg["rootPassword"]) {
header("Location:login-1.php?aviso=Error: Nombre de usuario y/o contraseña incorrectos");
exit;
}
$_SESSION["conectado"] = true;
header("Location:../index.php");
exit;
En este ejercicio se deben poder crear muchos usuarios distintos, con su nombre y contraseña. Todos los usuarios pueden realizar las mismas acciones.
Definimos dos tamaños de contraseña: el tamaño del control que el usuario escribe en el formulario y el tamaño del hash que se guarda en la base de datos
// Tamaño de los campos en la tabla Usuarios
$cfg["tablaUsuariosTamUsuario"] = 20; // Tamaño de la columna Usuarios > Nombre de usuario
$cfg["tablaUsuariosTamPassword"] = 64; // Tamaño de la columna Usuarios > Contraseña de usuario (cifrada)
// Tamaño de los controles en los formularios
$cfg["formUsuariosTamUsuario"] = $cfg["tablaUsuariosTamUsuario"]; // Tamaño de la caja de texto Usuario > Nombre de usuario
$cfg["formUsuariosTamPassword"] = 20; // Tamaño de la caja de texto Usuario > Contraseña
Modificamos la función borraTodo() para que borre y cree todas las tablas.
Una vez creadas las tablas, insertaremos un registro en la tabla de usuarios para que haya algún usuario que pueda conectarse. Los datos de ese usuario se encuentran como antes en el archivo de configuración. Indicaremos el campo id para asegurar el valor del campo. En este caso, no es algo estrictamente necesario, pero sí que lo sería si insertáramos registros que estuvieran relacionados con registros de otras tablas (como haremos más adelante).
function borraTodo()
{
...
$consulta = "DROP TABLE IF EXISTS $cfg[tablaUsuarios]";
...
$consulta = "DROP TABLE IF EXISTS $cfg[tablaPersonas]";
...
$consulta = "CREATE TABLE $cfg[tablaUsuarios] (
id INTEGER PRIMARY KEY,
usuario VARCHAR($cfg[tablaUsuariosTamUsuario]),
password VARCHAR($cfg[tablaUsuariosTamPassword])
)";
...
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(id, usuario, password)
VALUES (1, '$cfg[rootName]', '$cfg[rootPassword]')";
...
$consulta = "CREATE TABLE $cfg[tablaPersonas] (
id INTEGER PRIMARY KEY,
nombre VARCHAR($cfg[tablaPersonasTamNombre]) COLLATE NOCASE,
apellidos VARCHAR($cfg[tablaPersonasTamApellidos]) COLLATE NOCASE,
telefono VARCHAR($cfg[tablaPersonasTamTelefono]) COLLATE NOCASE,
correo VARCHAR($cfg[tablaPersonasTamCorreo]) COLLATE NOCASE
)";
}
Añadimos la información sobre la tabla de usuarios
// SQLITE: Nombres de las tablas
$cfg["tablaPersonas"] = "personas"; // Nombre de la tabla Personas
$cfg["tablaUsuarios"] = "usuarios"; // Nombre de la tabla Usuarios
Modificaríamos esta biblioteca de forma similar.
function borraTodo()
{
...
$consulta = "DROP DATABASE IF EXISTS $cfg[mysqlDatabase]";
...
$consulta = "CREATE DATABASE $cfg[mysqlDatabase]
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci";
...
$consulta = "CREATE TABLE $cfg[tablaUsuarios] (
id INTEGER UNSIGNED AUTO_INCREMENT,
usuario VARCHAR($cfg[tablaUsuariosTamUsuario]),
password VARCHAR($cfg[tablaUsuariosTamPassword]),
PRIMARY KEY(id)
)";
...
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(usuario, password)
VALUES ($cfg[rootName]', '$cfg[rootPassword]')";
...
$consulta = "CREATE TABLE $cfg[tablaPersonas] (
id INTEGER UNSIGNED AUTO_INCREMENT,
nombre VARCHAR($cfg[tablaPersonasTamNombre]),
apellidos VARCHAR($cfg[tablaPersonasTamApellidos]),
telefono VARCHAR($cfg[tablaPersonasTamTelefono]),
correo VARCHAR($cfg[tablaPersonasTamCorreo]),
PRIMARY KEY(id)
)";
}
// Valores de ordenación de las tablas
$cfg["tablaPersonasColumnasOrden"] = [
"nombre ASC", "nombre DESC",
"apellidos ASC", "apellidos DESC",
"telefono ASC", "telefono DESC",
"correo ASC", "correo DESC",
];
$cfg["tablaUsuariosColumnasOrden"] = [
"usuario ASC", "usuario DESC",
"password ASC", "password DESC",
];
Crearemos una carpeta /administrador y moveremos las páginas borrar-todo-1.php y borrar-todo-2.php a esa carpeta (habrá modificar los menús).
} elseif ($menu == MENU_ADMINISTRADOR) {
print " <li><a href=\"../index.php\">Volver</a></li>\n";
print " <li><a href=\"borrar-todo-1.php\">Borrar todo</a></li>\n";
} elseif ($menu == MENU_USUARIOS) {
print " <li><a href=\"../../index.php\">Volver</a></li>\n";
print " <li><a href=\"insertar-1.php\">Añadir registro</a></li>\n";
print " <li><a href=\"listar.php\">Listar</a></li>\n";
print " <li><a href=\"borrar-1.php\">Borrar</a></li>\n";
print " <li><a href=\"buscar-1.php\">Buscar</a></li>\n";
print " <li><a href=\"modificar-1.php\">Modificar</a></li>\n";
} elseif ($menu == MENU_PERSONAS) {
print " <li><a href=\"../../index.php\">Volver</a></li>\n";
print " <li><a href=\"insertar-1.php\">Añadir registro</a></li>\n";
print " <li><a href=\"listar.php\">Listar</a></li>\n";
print " <li><a href=\"borrar-1.php\">Borrar</a></li>\n";
print " <li><a href=\"buscar-1.php\">Buscar</a></li>\n";
print " <li><a href=\"modificar-1.php\">Modificar</a></li>\n";
} else {
...
Añadimos una variable de configuración para permitir o no modificar la contraseña del usuario Administrador:
$cfg["rootPasswordModificable"] = false; // Contraseña del usuario Administrador se puede cambiar o no
Debemos definir un nivel más de profundidad:
define("PROFUNDIDAD_0", ""); // Profundidad de nivel de la página: directorio raíz
define("PROFUNDIDAD_1", "../"); // Profundidad de nivel de la página: subdirectorio
define("PROFUNDIDAD_2", "../../"); // Profundidad de nivel de la página: sub-subdirectorio
Para insertar un registro de usuario, el campo nombre de usuario es obligatorio:
if ($usuario == "") {
print " <p class=\"aviso\">Hay que escribir un nombre de usuario.</p>\n";
print "\n";
} elseif (mb_strlen($usuario, "UTF-8") > $cfg["tablaUsuariosTamUsuario"]) {
print " <p class=\"aviso\">El nombre de usuario no puede tener más de $cfg[tablaUsuariosTamUsuario] caracteres.</p>\n";
print "\n";
} else {
$usuarioOk = true;
}
No puede haber dos usuarios con el mismo nombre de usuario:
$consulta = "SELECT COUNT(*) FROM $cfg[tablaUsuarios]
WHERE usuario = :usuario";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif ($resultado->fetchColumn() > 0) {
print " <p class=\"aviso\">Ya existe un usuario con ese nombre.</p>\n";
} else {
...
En la tabla guardamos la contraseña encriptada:
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(usuario, password)
VALUES (:usuario, :password)";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario, ":password" => encripta($password)])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro creado correctamente.</p>\n";
}
El usuario inicial no se puede borrar (esto no es estrictamente necesario, pero yo lo hago para que la aplicación que tengo incluida en la página de ejercicios de los apuntes esté siempre accesible a los visitantes de la web):
foreach ($id as $indice => $valor) {
$consulta = "SELECT * FROM $cfg[tablaUsuarios]
WHERE id = :indice";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":indice" => $indice])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!($registro = $resultado->fetch())) {
print " <p class=\"aviso\">Registro no encontrado.</p>\n";
} elseif ($registro["usuario"] == $cfg["rootName"]) {
print " <p class=\"aviso\">Este usuario no se puede borrar.</p>\n";
} else {
...
No se incluye el campo contraseña en el formulario. Buscar por contraseña no tiene demasiado sentido porque en la base de datos se guarda el hash de la contraseña.
El único motivo para buscar por contraseñas podría ser para ver qué usuarios tienen la contraseña en blanco (o contraseñas triviales como 1234, por ejemplo). Pero como la búsqueda tiene en cuenta todos los campos, incluir la contraseña en la búsqueda reduce completamente los resultados de la búsqueda.
El nombre del Administrador inicial no se puede cambiar (su nombre se establece en la variable de configuración $cfg["rootName"]). Su contraseña se puede cambiar o no dependiendo del valor de la variable de configuración $cfg["rootPasswordModificable"] (true o false). El bloqueo del cambio de contraseña lo he incluido para que la aplicación que tengo incluida en la página de ejercicios de los apuntes esté siempre accesible a los visitantes de la web:
$consulta = "SELECT * FROM $cfg[tablaUsuarios]
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!($registro = $resultado->fetch())) {
print " <p class=\"aviso\">Registro no encontrado.</p>\n";
} elseif ($registro["usuario"] == $cfg["rootName"] && !$cfg["rootPasswordModificable"]) {
print " <p class=\"aviso\">Este usuario no se puede modificar.</p>\n";
} else {
...
Al mostrar los valores de los campos, el campo nivel es un menú <select>. Para que se muestre la opción correspondiente al nivel actual del usuario debemos añadir el atributo selected al <option> correspondiente:
foreach ($cfg["usuariosNiveles"] as $indice => $valor) {
if ($registro["nivel"] == $indice) {
print " <option value=\"$indice\" selected>$valor</option>\n";
} else {
print " <option value=\"$indice\">$valor</option>\n";
}
}
Al cambiar el nombre del usuario hay que comprobar antes que el nuevo nombre no esté ya registrado
// La consulta cuenta los registros con un id diferente porque MySQL no distingue
// mayúsculas de minúsculas y si en un registro sólo se cambian mayúsculas por
// minúsculas MySQL diría que ya hay un registro como el que se quiere guardar.
$consulta = "SELECT COUNT(*) FROM $cfg[tablaUsuarios]
WHERE usuario = :usuario
AND id<>:id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario, ":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif ($resultado->fetchColumn() > 0) {
print " <p class=\"aviso\">Ya existe un registro con esos mismos valores. "
. "No se ha guardado la modificación.</p>\n";
} else {
...
El nombre del Administrador inicial no se puede cambiar y la contraseña solo puede cambiarse si la variable de configuración $cfg["rootPasswordModificable"] es true.:
$consulta = "SELECT * FROM $cfg[tablaUsuarios]
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
$registro = $resultado->fetch();
if ($registro["usuario"] == $cfg["rootName"] && (!$cfg["rootPasswordModificable"] || $registro["usuario"] != $usuario)) {
print " <p class=\"aviso\">Del usuario Administrador inicial sólo se puede cambiar la contraseña.</p>\n";
} else {
...
function existenTablas()
{
global $pdo, $cfg;
$existe = true;
foreach ($cfg["dbTablas"] as $tabla) {
$consulta = "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = '$tabla'";
$resultado = $pdo->query($consulta);
if (!$resultado) {
$existe = false;
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
print "\n";
} else {
if ($resultado->fetchColumn() == 0) {
$existe = false;
}
}
}
return $existe;
}
function existenTablas()
{
global $pdo, $cfg;
$existe = true;
$consulta = "SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = '$cfg[mysqlDatabase]'";
$resultado = $pdo->query($consulta);
if (!$resultado) {
$existe = false;
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
print "\n";
} else {
if ($resultado->fetchColumn() == 0) {
$existe = false;
} else {
foreach ($cfg["dbTablas"] as $tabla) {
// En information_schema.tables los nombres de las tablas no llevan el nombre
// de la base de datos, así que lo elimino
$tabla = str_replace("$cfg[mysqlDatabase].", "", $tabla);
$consulta = "SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = '$cfg[mysqlDatabase]'
AND table_name = '$tabla'";
$resultado = $pdo->query($consulta);
if (!$resultado) {
$existe = false;
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
print "\n";
} else {
if ($resultado->fetchColumn() == 0) {
$existe = false;
}
}
}
}
}
return $existe;
}
if (!existenTablas()) {
print "<p>La base de datos no está creada. Se creará la base de datos.</p>\n";
print "\n";
borraTodo();
}
En este ejercicio se deben definir dos categorías de usuarios:
define("NIVEL_USUARIO_BASICO", 10); // Usuario web de nivel Usuario Básico
define("NIVEL_ADMINISTRADOR", 20); // Usuario web de nivel Administrador
// Niveles de usuario
$cfg["usuariosNiveles"] = [
NIVEL_USUARIO_BASICO => "Usuario Básico",
NIVEL_ADMINISTRADOR => "Administrador",
];
Añadimos el campo nivel en los posibles valores de ordenación de la tabla Usuarios.
$cfg["tablaUsuariosColumnasOrden"] = [
"usuario ASC", "usuario DESC",
"password ASC", "password DESC",
"nivel ASC", "nivel DESC",
];
$consulta = "CREATE TABLE $cfg[tablaUsuarios] (
id INTEGER PRIMARY KEY,
usuario VARCHAR($cfg[tablaUsuariosTamUsuario]),
password VARCHAR($cfg[tablaUsuariosTamPassword]),
nivel INTEGER
)";
if (!$pdo->query($consulta)) {
print " <p class=\"aviso\">Error al crear la tabla Usuarios. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Tabla Usuarios creada correctamente.</p>\n";
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(id, usuario, password, nivel)
VALUES (1, '$cfg[rootName]', '$cfg[rootPassword]', " . NIVEL_ADMINISTRADOR . ")";
...
$consulta = "CREATE TABLE $cfg[tablaUsuarios] (
id INTEGER UNSIGNED AUTO_INCREMENT,
usuario VARCHAR($cfg[tablaUsuariosTamUsuario]),
password VARCHAR($cfg[tablaUsuariosTamPassword]),
nivel INTEGER NOT NULL,
PRIMARY KEY(id)
)";
if (!$pdo->query($consulta)) {
print " <p class=\"aviso\">Error al crear la tabla Usuarios. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Tabla creada correctamente.</p>\n";
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(id, usuario, password, nivel)
VALUES (1, '$cfg[rootName]', '$cfg[rootPassword]', " . NIVEL_ADMINISTRADOR . ")";
...
$_SESSION["conectado"] = true;
$_SESSION["nivel"] = $registro["nivel"];
session_name($cfg["sessionName"]);
session_start();
if (!isset($_SESSION["conectado"]) || $_SESSION["nivel"] < NIVEL_ADMINISTRADOR) {
header("Location:../index.php");
exit;
}
En las páginas accesibles a los usuarios básicos y administradores [/db/tabla-personas] comprobamos si la variable de sesión tiene el nivel requerido (aunque realmente sería suficiente con comprobar que existe $_SESSION["conectado"] pero de esta manera explicitamos el nivel requerido):
session_name($cfg["sessionName"]);
session_start();
if (!isset($_SESSION["conectado"]) || $_SESSION["nivel"] < NIVEL_USUARIO_BASICO) {
header("Location:../../index.php");
exit;
}
...
} elseif ($_SESSION["nivel"] == NIVEL_USUARIO_BASICO) {
if ($menu == MENU_PRINCIPAL) {
print " <li><a href=\"db/tabla-personas/index.php\">Personas</a></li>\n";
print " <li><a href=\"acceso/logout.php\">Desconectarse</a></li>\n";
} elseif ($menu == MENU_VOLVER) {
print " <li><a href=\"../index.php\">Volver</a></li>\n";
} elseif ($menu == MENU_PERSONAS) {
print " <li><a href=\"../../index.php\">Volver</a></li>\n";
print " <li><a href=\"insertar-1.php\">Añadir registro</a></li>\n";
print " <li><a href=\"listar.php\">Listar</a></li>\n";
print " <li><a href=\"borrar-1.php\">Borrar</a></li>\n";
print " <li><a href=\"buscar-1.php\">Buscar</a></li>\n";
print " <li><a href=\"modificar-1.php\">Modificar</a></li>\n";
} else {
print " <li>Error en la selección de menú</li>\n";
}
} elseif ($_SESSION["nivel"] == NIVEL_ADMINISTRADOR) {
...
}
Debemos añadir en las diferentes páginas el campo nivel. Por ejemplo:
print " <tr>\n";
print " <td>Nivel:</td>\n";
print " <td>\n";
print " <select name=\"nivel\">\n";
foreach ($cfg["usuariosNiveles"] as $indice => $valor) {
print " <option value=\"$indice\">$valor</option>\n";
}
print " </select>\n";
print " </td>\n";
print " </tr>\n";
$nivel = recoge("nivel");
...
$nivelOk = false;
...
if ($nivel == "") {
print " <p class=\"aviso\">Hay que seleccionar un nivel de usuario.</p>\n";
print "\n";
} elseif (!array_key_exists($nivel, $cfg["usuariosNiveles"])) {
print " <p class=\"aviso\">Nivel de usuario incorrecto.</p>\n";
print "\n";
} else {
$nivelOk = true;
}
if ($usuarioOk && $passwordOk && $nivelOk) {
if ($usuario == "" || $password == "" || $nivel == "") {
print " <p>Hay que rellenar todos los campos. No se ha guardado el registro.</p>\n";
} else {
...
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(usuario, password, nivel)
VALUES (:usuario, :password, :nivel)";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario, ":password" => encripta($password), ":nivel" => $nivel])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro creado correctamente.</p>\n";
}
...
...
foreach ($registros as $registro) {
print " <tr>\n";
print " <td>$registro[usuario]</td>\n";
print " <td>$registro[password]</td>\n";
print " <td>{$cfg["usuariosNiveles"][$registro["nivel"]]}</td>\n";
print " </tr>\n";
}
...
print " <tr>\n";
print " <td>Nivel:</td>\n";
print " <td>\n";
print " <select name=\"nivel\">\n";
print " <option value=\"\"></option>\n";
foreach ($cfg["usuariosNiveles"] as $indice => $valor) {
print " <option value=\"$indice\">$valor</option>\n";
}
print " </select>\n";
print " </td>\n";
print " </tr>\n";
$consulta = "SELECT * FROM $cfg[tablaUsuarios]
WHERE usuario LIKE :usuario
AND CAST(nivel AS VARCHAR) LIKE :nivel
ORDER BY $ordena";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => "%$usuario%", ":nivel" => "%$nivel%"])) {
...
Podemos incluir un control para que el usuario indique si quiere incluir registros de prueba en la base de datos ...
print " <p><label><input type=\"checkbox\" name=\"demo\" value=\"Sí\"> Incluir datos de prueba</label></p>\n";
... recogerlo y en caso afirmativo, llamar a una función que incluya los registros de prueba:
$demo = recoge("demo");
...
if ($demo == "Sí") {
insertaDemo();
}
Podemos indicar los registros a crear en una matriz e insertarlos recorriendo la matriz. En el ejemplo, los registros incluyen incluso el valor del id. En este caso no es necesario, pero sí lo sería si los registros de una tabla hicieran referencia a los registros de otra tabla, como ocurre en el ejercicio 4, por lo que preparamos ya el código para esa situación.
// Registros de prueba opcionales
$cfg["registrosDemo"] = [
[$cfg["tablaUsuarios"], [2, "usuario1", encripta("usuario1"), NIVEL_USUARIO_BASICO]],
[$cfg["tablaUsuarios"], [3, "usuario2", encripta("usuario2"), NIVEL_USUARIO_BASICO]],
[$cfg["tablaUsuarios"], [4, "admin1", encripta("admin1"), NIVEL_ADMINISTRADOR]],
[$cfg["tablaPersonas"], [1, "Pepito", "Conejo", "271828182", "pepito.conejo@example.com"]],
[$cfg["tablaPersonas"], [2, "Numa", "Nigerio", "161803398", "numa.nigerio@example.com"]],
[$cfg["tablaPersonas"], [3, "Fulanito", "Mengánez", "314159265", "fulanito.menganez@example.com"]],
];
function insertaDemo()
{
global $cfg, $pdo;
print " <p>Insertando registros de prueba ...</p>\n";
print "\n";
foreach ($cfg["registrosDemo"] as $registro) {
if ($registro[0] == $cfg["tablaUsuarios"]) {
$consulta = "INSERT INTO $cfg[tablaUsuarios]
(id, usuario, password, nivel)
VALUES ({$registro[1][0]}, '{$registro[1][1]}', '{$registro[1][2]}', {$registro[1][3]})";
} elseif ($registro[0] == $cfg["tablaPersonas"]) {
$consulta = "INSERT INTO $cfg[tablaPersonas]
(id, nombre, apellidos, telefono, correo)
VALUES ({$registro[1][0]}, '{$registro[1][1]}', '{$registro[1][2]}', '{$registro[1][3]}', '{$registro[1][4]}')";
}
if (!$pdo->query($consulta)) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro creado correctamente.</p>\n";
}
print "\n";
}
}
Podemos aplicar la función dirname(), que devuelve el camino padre (en este caso, elimina el nombre del archivo), a la variable de configuración $cfg["sqliteDatabase"]. Y a su resultado la función is_dir(), que indica si el camino es un directorio (es decir, si existe).
function conectaDb()
{
global $cfg;
if (!is_dir(dirname($cfg["sqliteDatabase"]))) {
print " <p class=\"aviso\">Error: El directorio <strong>" . dirname($cfg["sqliteDatabase"]) . "</strong> no está disponible.</p>\n";
exit;
} else {
...
En el formulario incluimos una casilla de verificación ...
print " <tr>\n";
print " <td>Contraseña:</td>\n";
print " <td>\n";
print " <input type=\"text\" name=\"password\" size=\"$cfg[formUsuariosTamPassword]\" maxlength=\"$cfg[formUsuariosTamPassword]\">\n";
print " <input type=\"checkbox\" name=\"mantenerPassword\" value=\"Sí\"> Mantener contraseña actual\n";
print " </td>\n";
print " </tr>\n";
... recogemos el control y en función de su valor mantenemos o cambiamos la contraseña:
$mantenerPassword = recoge("mantenerPassword");
...
} elseif ($mantenerPassword == "Sí") {
$consulta = "UPDATE $cfg[tablaUsuarios]
SET usuario = :usuario, nivel = :nivel
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario, ":nivel" => $nivel, ":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro modificado correctamente.</p>\n";
}
} else {
$consulta = "UPDATE $cfg[tablaUsuarios]
SET usuario = :usuario, password = :password, nivel = :nivel
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":usuario" => $usuario, ":password" => encripta($password), ":nivel" => $nivel, ":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro modificado correctamente.</p>\n";
}
Modificamos la condición de superación del número máximo de registros para que no se cumpla si la variable de configuración no es mayor que 0:
...
} elseif ($resultado->fetchColumn() >= $cfg["tablaUsuariosMaxReg"] && $cfg["tablaUsuariosMaxReg"] > 0) {
...