From 33068b1356ddf8d65ed9d4569e747ecff0588190 Mon Sep 17 00:00:00 2001 From: Pinga <121483313+getpinga@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:12:28 +0200 Subject: [PATCH] Added ability to update user accounts --- cp/app/Controllers/UsersController.php | 155 ++++++++++++++++++ .../views/admin/users/updateUser.twig | 126 ++++++++++++++ cp/resources/views/layouts/app.twig | 2 +- cp/resources/views/partials/js-users.twig | 13 +- cp/routes/web.php | 2 + 5 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 cp/resources/views/admin/users/updateUser.twig diff --git a/cp/app/Controllers/UsersController.php b/cp/app/Controllers/UsersController.php index ecfd725..5661ab1 100644 --- a/cp/app/Controllers/UsersController.php +++ b/cp/app/Controllers/UsersController.php @@ -172,4 +172,159 @@ class UsersController extends Controller 'registrar' => $registrar, ]); } + + public function updateUser(Request $request, Response $response, $args) + { + $db = $this->container->get('db'); + // Get the current URI + $uri = $request->getUri()->getPath(); + $registrars = $db->select("SELECT id, clid, name FROM registrar"); + + if ($args) { + $args = trim($args); + + if (!preg_match('/^[a-z0-9_-]+$/', $args)) { + $this->container->get('flash')->addMessage('error', 'Invalid user name'); + return $response->withHeader('Location', '/users')->withStatus(302); + } + + $user = $db->selectRow('SELECT id,email,username,status,verified,roles_mask,registered,last_login FROM users WHERE username = ?', + [ $args ]); + $user_asso = $db->selectValue('SELECT registrar_id FROM registrar_users WHERE user_id = ?', + [ $user['id'] ]); + $registrar_name = $db->selectValue('SELECT name FROM registrar WHERE id = ?', + [ $user_asso ]); + + if ($user) { + // Check if the user is not an admin (assuming role 0 is admin) + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + $_SESSION['user_to_update'] = [$args]; + + return view($response,'admin/users/updateUser.twig', [ + 'user' => $user, + 'currentUri' => $uri, + 'registrars' => $registrars, + 'user_asso' => $user_asso, + 'registrar_name' => $registrar_name + ]); + } else { + // User does not exist, redirect to the users view + return $response->withHeader('Location', '/users')->withStatus(302); + } + } else { + // Redirect to the users view + return $response->withHeader('Location', '/users')->withStatus(302); + } + } + + public function updateUserProcess(Request $request, Response $response) + { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + if ($request->getMethod() === 'POST') { + // Retrieve POST data + $data = $request->getParsedBody(); + $db = $this->container->get('db'); + + $email = $data['email'] ?? null; + $old_username = $_SESSION['user_to_update'][0]; + $username = $data['username'] ?? null; + $password = $data['password'] ?? null; + $password_confirmation = $data['password_confirmation'] ?? null; + $status = $data['status'] ?? null; + $verified = $data['verified'] ?? null; + + // Define validation rules + $validators = [ + 'email' => v::email()->notEmpty()->setName('Email'), + 'username' => v::regex('/^[a-zA-Z0-9_-]+$/')->length(3, 20)->setName('Username'), + 'status' => v::in(['0', '1'])->setName('Status'), + 'verified' => v::in(['0', '1'])->setName('Verified'), // Ensure verified is checked as 0 or 1 + ]; + + // Add password validation only if provided + if (!empty($password)) { + $validators['password'] = v::stringType()->notEmpty()->length(6, 255)->setName('Password'); + + // Add password confirmation check only if both fields are provided + if (!empty($password_confirmation)) { + $validators['password_confirmation'] = v::equals($password)->setName('Password Confirmation'); + } + } + + // Validate data + $errors = []; + foreach ($validators as $field => $validator) { + try { + $validator->assert($data[$field] ?? null); + } catch (\Respect\Validation\Exceptions\ValidationException $exception) { + $errors[$field] = $exception->getMessages(); // Collect all error messages + } + } + + // If errors exist, return with errors + if (!empty($errors)) { + // Flatten the errors array into a string + $errorMessages = []; + foreach ($errors as $field => $fieldErrors) { + $fieldMessages = implode(', ', $fieldErrors); // Concatenate messages for the field + $errorMessages[] = ucfirst($field) . ': ' . $fieldMessages; // Prefix with field name + } + $errorString = implode('; ', $errorMessages); // Join all fields' errors + + // Add the flattened error string as a flash message + $this->container->get('flash')->addMessage('error', 'Error: ' . $errorString); + return $response->withHeader('Location', '/user/update/'.$old_username)->withStatus(302); + } + + if (empty($email)) { + $this->container->get('flash')->addMessage('error', 'No email specified for update'); + return $response->withHeader('Location', '/user/update/'.$old_username)->withStatus(302); + } + + $db->beginTransaction(); + + try { + $currentDateTime = new \DateTime(); + $update = $currentDateTime->format('Y-m-d H:i:s.v'); + + // Prepare the data to update + $updateData = [ + 'email' => $email, + 'username' => $username, + 'verified' => $verified, + 'status' => $status, + ]; + + if (!empty($password)) { + $password_hashed = password_hash($password, PASSWORD_ARGON2ID, ['memory_cost' => 1024 * 128, 'time_cost' => 6, 'threads' => 4]); + $updateData['password'] = $password_hashed; + } + + $db->update( + 'users', + $updateData, + [ + 'username' => $old_username + ] + ); + + $db->commit(); + } catch (Exception $e) { + $db->rollBack(); + $this->container->get('flash')->addMessage('error', 'Database failure during update: ' . $e->getMessage()); + return $response->withHeader('Location', '/user/update/'.$old_username)->withStatus(302); + } + + unset($_SESSION['user_to_update']); + $this->container->get('flash')->addMessage('success', 'User ' . $username . ' has been updated successfully on ' . $update); + return $response->withHeader('Location', '/user/update/'.$username)->withStatus(302); + } + } + } \ No newline at end of file diff --git a/cp/resources/views/admin/users/updateUser.twig b/cp/resources/views/admin/users/updateUser.twig new file mode 100644 index 0000000..cbf525e --- /dev/null +++ b/cp/resources/views/admin/users/updateUser.twig @@ -0,0 +1,126 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Update User') }}{% endblock %} + +{% block content %} +