diff --git a/cp/app/Controllers/LogsController.php b/cp/app/Controllers/LogsController.php index 8b26f99..d8602cc 100644 --- a/cp/app/Controllers/LogsController.php +++ b/cp/app/Controllers/LogsController.php @@ -13,14 +13,18 @@ class LogsController extends Controller { return view($response,'admin/logs/index.twig'); } - + public function poll(Request $request, Response $response) { return view($response,'admin/logs/poll.twig'); } - + public function log(Request $request, Response $response) { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + return view($response,'admin/logs/log.twig'); } } \ No newline at end of file diff --git a/cp/app/Controllers/RegistrarsController.php b/cp/app/Controllers/RegistrarsController.php index fd25fee..17aad09 100644 --- a/cp/app/Controllers/RegistrarsController.php +++ b/cp/app/Controllers/RegistrarsController.php @@ -12,6 +12,10 @@ class RegistrarsController extends Controller { public function view(Request $request, Response $response) { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + return view($response,'admin/registrars/index.twig'); } @@ -303,7 +307,7 @@ class RegistrarsController extends Controller [ $args ]); if ($registrar) { - // Check if the user is not an admin (assuming role 0 is admin) + // Check if the user is not an admin if ($_SESSION["auth_roles"] != 0) { $userRegistrars = $db->select('SELECT registrar_id FROM registrar_users WHERE user_id = ?', [$_SESSION['auth_user_id']]); @@ -311,7 +315,7 @@ class RegistrarsController extends Controller $userRegistrarIds = array_column($userRegistrars, 'registrar_id'); // Check if the registrar's ID is in the user's list of registrar IDs - if (!in_array($registrars['id'], $userRegistrarIds)) { + if (!in_array($registrar['id'], $userRegistrarIds)) { // Redirect to the registrars view if the user is not authorized for this contact return $response->withHeader('Location', '/registrars')->withStatus(302); } @@ -359,6 +363,58 @@ class RegistrarsController extends Controller } + public function registrar(Request $request, Response $response) + { + $db = $this->container->get('db'); + // Get the current URI + $uri = $request->getUri()->getPath(); + $registrarId = $_SESSION['auth_registrar_id']; + + if (isset($registrarId) && $registrarId !== "") { + $registrar = $db->selectRow('SELECT * FROM registrar WHERE id = ?', + [ $registrarId ]); + + if ($registrar) { + $registrarContact = $db->selectRow('SELECT * FROM registrar_contact WHERE registrar_id = ?', + [ $registrar['id'] ]); + $registrarOte = $db->select('SELECT * FROM registrar_ote WHERE registrar_id = ? ORDER by command', + [ $registrar['id'] ]); + $registrarUsers = $db->selectRow('SELECT user_id FROM registrar_users WHERE registrar_id = ?', + [ $registrar['id'] ]); + $userEmail = $db->selectRow('SELECT email FROM users WHERE id = ?', + [ $registrarUsers['user_id'] ]); + $registrarWhitelist = $db->select('SELECT addr FROM registrar_whitelist WHERE registrar_id = ?', + [ $registrar['id'] ]); + // Check if RegistrarOTE is not empty + if (is_array($registrarOte) && !empty($registrarOte)) { + // Split the results into two groups + $firstHalf = array_slice($registrarOte, 0, 5); + $secondHalf = array_slice($registrarOte, 5); + } else { + // If RegistrarOTE is empty, set both halves to empty arrays + $firstHalf = []; + $secondHalf = []; + } + + return view($response,'admin/registrars/viewRegistrar.twig', [ + 'registrar' => $registrar, + 'registrarContact' => $registrarContact, + 'firstHalf' => $firstHalf, + 'secondHalf' => $secondHalf, + 'userEmail' => $userEmail, + 'registrarWhitelist' => $registrarWhitelist, + 'currentUri' => $uri + ]); + } else { + // Contact does not exist, redirect to the dashboard view + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + } else { + // Redirect to the registrars view + return $response->withHeader('Location', '/registrars')->withStatus(302); + } + } + public function updateRegistrar(Request $request, Response $response, $args) { $db = $this->container->get('db'); @@ -518,11 +574,6 @@ class RegistrarsController extends Controller try { $currentDateTime = new \DateTime(); $update = $currentDateTime->format('Y-m-d H:i:s.v'); - $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $randomPrefix = ''; - for ($i = 0; $i < 2; $i++) { - $randomPrefix .= $characters[rand(0, strlen($characters) - 1)]; - } $currency = $_SESSION['_currency'] ?? 'USD'; if (empty($data['ianaId']) || !is_numeric($data['ianaId'])) { @@ -667,5 +718,287 @@ class RegistrarsController extends Controller return $response->withHeader('Location', '/registrar/update/'.$registrar)->withStatus(302); } } + + public function editRegistrar(Request $request, Response $response) + { + if ($request->getMethod() === 'POST') { + // Retrieve POST data + $data = $request->getParsedBody(); + $db = $this->container->get('db'); + if (!empty($_SESSION['registrars_to_update'])) { + $registrar = $_SESSION['registrars_to_update'][0]; + } else { + $this->container->get('flash')->addMessage('error', 'No registrar specified for update'); + return $response->withHeader('Location', '/registrars')->withStatus(302); + } + + $data['ipAddress'] = array_filter($data['ipAddress']); + $iso3166 = new ISO3166(); + $countries = $iso3166->all(); + + $ipAddressValidator = v::when( + v::arrayType()->notEmpty(), // Condition: If it's a non-empty array + v::arrayType()->each(v::ip()), // Then: Each element must be a valid IP address + v::equals('') // Else: Allow it to be an empty string + ); + + $data['owner']['cc'] = strtoupper($data['owner']['cc']); + $data['billing']['cc'] = strtoupper($data['billing']['cc']); + $data['abuse']['cc'] = strtoupper($data['abuse']['cc']); + + $phoneValidator = v::regex('/^\+\d{1,3}\.\d{2,12}$/'); + + // Define validation for nested fields + $contactValidator = [ + v::key('first_name', v::stringType()->notEmpty()->length(1, 255), true), + v::key('last_name', v::stringType()->notEmpty()->length(1, 255), true), + v::key('org', v::optional(v::stringType()->length(1, 255)), false), + v::key('street1', v::optional(v::stringType()), false), + v::key('city', v::stringType()->notEmpty(), true), + v::key('sp', v::optional(v::stringType()), false), + v::key('pc', v::optional(v::stringType()), false), + v::key('cc', v::countryCode(), true), + v::key('voice', v::optional($phoneValidator), false), + v::key('fax', v::optional(v::phone()), false), + v::key('email', v::email(), true) + ]; + + $validators = [ + 'name' => v::stringType()->notEmpty()->length(1, 255), + 'ianaId' => v::optional(v::positive()->length(1, 5)), + 'email' => v::email(), + 'owner' => v::optional(v::keySet(...$contactValidator)), + 'billing' => v::optional(v::keySet(...$contactValidator)), + 'abuse' => v::optional(v::keySet(...$contactValidator)), + 'whoisServer' => v::domain(), + 'rdapServer' => v::domain(), + 'url' => v::url(), + 'abuseEmail' => v::email(), + 'abusePhone' => v::optional($phoneValidator), + 'ipAddress' => v::optional($ipAddressValidator) + ]; + + $errors = []; + foreach ($validators as $field => $validator) { + try { + $validator->assert(isset($data[$field]) ? $data[$field] : []); + } catch (\Respect\Validation\Exceptions\NestedValidationException $e) { + $errors[$field] = $e->getMessages(); + } + } + + if (!empty($errors)) { + // Handle errors + $errorText = ''; + + foreach ($errors as $field => $messages) { + $errorText .= ucfirst($field) . ' errors: ' . implode(', ', $messages) . '; '; + } + + // Trim the final semicolon and space + $errorText = rtrim($errorText, '; '); + + $this->container->get('flash')->addMessage('error', $errorText); + return $response->withHeader('Location', '/registrar/edit')->withStatus(302); + } + + if (!empty($_SESSION['registrars_user_email'])) { + $regEmail = $_SESSION['registrars_user_email'][0]; + } else { + $this->container->get('flash')->addMessage('error', 'No email specified for update'); + return $response->withHeader('Location', '/registrar/edit')->withStatus(302); + } + + $db->beginTransaction(); + + try { + $currentDateTime = new \DateTime(); + $update = $currentDateTime->format('Y-m-d H:i:s.v'); + $currency = $_SESSION['_currency'] ?? 'USD'; + + if (empty($data['ianaId']) || !is_numeric($data['ianaId'])) { + $data['ianaId'] = null; + } + + $updateData = [ + 'name' => $data['name'], + 'iana_id' => $data['ianaId'], + 'email' => $data['email'], + 'url' => $data['url'], + 'whois_server' => $data['whoisServer'], + 'rdap_server' => $data['rdapServer'], + 'abuse_email' => $data['abuseEmail'], + 'abuse_phone' => $data['abusePhone'], + 'currency' => $currency, + 'lastupdate' => $crdate + ]; + + if (!empty($data['eppPassword'])) { + $eppPassword = password_hash($data['eppPassword'], PASSWORD_ARGON2ID, ['memory_cost' => 1024 * 128, 'time_cost' => 6, 'threads' => 4]); + $updateData['pw'] = $eppPassword; + } + + $db->update( + 'registrar', + $updateData, + [ + 'clid' => $registrar + ] + ); + $registrar_id = $db->selectValue( + 'SELECT id FROM registrar WHERE clid = ?', + [$registrar] + ); + + $db->update( + 'registrar_contact', + [ + 'first_name' => $data['owner']['first_name'], + 'last_name' => $data['owner']['last_name'], + 'org' => $data['owner']['org'], + 'street1' => $data['owner']['street1'], + 'city' => $data['owner']['city'], + 'sp' => $data['owner']['sp'], + 'pc' => $data['owner']['pc'], + 'cc' => strtolower($data['owner']['cc']), + 'voice' => $data['owner']['voice'], + 'email' => $data['owner']['email'] + ], + [ + 'registrar_id' => $registrar_id, + 'type' => 'owner' + ] + ); + + $db->update( + 'registrar_contact', + [ + 'first_name' => $data['billing']['first_name'], + 'last_name' => $data['billing']['last_name'], + 'org' => $data['billing']['org'], + 'street1' => $data['billing']['street1'], + 'city' => $data['billing']['city'], + 'sp' => $data['billing']['sp'], + 'pc' => $data['billing']['pc'], + 'cc' => strtolower($data['billing']['cc']), + 'voice' => $data['billing']['voice'], + 'email' => $data['billing']['email'] + ], + [ + 'registrar_id' => $registrar_id, + 'type' => 'billing' + ] + ); + + $db->update( + 'registrar_contact', + [ + 'first_name' => $data['abuse']['first_name'], + 'last_name' => $data['abuse']['last_name'], + 'org' => $data['abuse']['org'], + 'street1' => $data['abuse']['street1'], + 'city' => $data['abuse']['city'], + 'sp' => $data['abuse']['sp'], + 'pc' => $data['abuse']['pc'], + 'cc' => strtolower($data['abuse']['cc']), + 'voice' => $data['abuse']['voice'], + 'email' => $data['abuse']['email'] + ], + [ + 'registrar_id' => $registrar_id, + 'type' => 'abuse' + ] + ); + + if (!empty($data['ipAddress'])) { + $db->delete( + 'registrar_whitelist', + [ + 'registrar_id' => $registrar_id + ] + ); + + foreach ($data['ipAddress'] as $ip) { + $db->insert( + 'registrar_whitelist', + [ + 'registrar_id' => $registrar_id, + 'addr' => $ip + ] + ); + } + } + + if ($data['panelPassword']) { + $panelPassword = password_hash($data['panelPassword'], PASSWORD_ARGON2ID, ['memory_cost' => 1024 * 128, 'time_cost' => 6, 'threads' => 4]); + + $db->update( + 'users', + [ + 'password' => $panelPassword, + ], + [ + 'email' => $regEmail + ] + ); + } + + $db->commit(); + } catch (Exception $e) { + $db->rollBack(); + $this->container->get('flash')->addMessage('error', 'Database failure during update: ' . $e->getMessage()); + return $response->withHeader('Location', '/registrar/edit')->withStatus(302); + } + + unset($_SESSION['registrars_to_update']); + unset($_SESSION['registrars_user_email']); + $this->container->get('flash')->addMessage('success', 'Registrar ' . $data['name'] . ' has been updated successfully on ' . $update); + return $response->withHeader('Location', '/registrar/edit')->withStatus(302); + } + + $db = $this->container->get('db'); + $iso3166 = new ISO3166(); + $countries = $iso3166->all(); + // Get the current URI + $uri = $request->getUri()->getPath(); + $registrarId = $_SESSION['auth_registrar_id']; + + if (isset($registrarId) && $registrarId !== "") { + $registrar = $db->selectRow('SELECT * FROM registrar WHERE id = ?', + [ $registrarId ]); + + if ($registrar) { + $contacts = $db->select("SELECT * FROM registrar_contact WHERE registrar_id = ?", + [ $registrar['id'] ]); + $ote = $db->select("SELECT * FROM registrar_ote WHERE registrar_id = ?", + [ $registrar['id'] ]); + $user_id = $db->selectValue("SELECT user_id FROM registrar_users WHERE registrar_id = ?", + [ $registrar['id'] ]); + $user = $db->selectRow("SELECT email FROM users WHERE id = ?", + [ $user_id ]); + $whitelist = $db->select("SELECT * FROM registrar_whitelist WHERE registrar_id = ?", + [ $registrar['id'] ]); + + $_SESSION['registrars_to_update'] = [$registrar['clid']]; + $_SESSION['registrars_user_email'] = [$user['email']]; + + return view($response,'admin/registrars/updateRegistrarUser.twig', [ + 'registrar' => $registrar, + 'contacts' => $contacts, + 'ote' => $ote, + 'user' => $user, + 'whitelist' => $whitelist, + 'currentUri' => $uri, + 'countries' => $countries + ]); + } else { + // Registrar does not exist, redirect to the dashboard view + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + } else { + // Redirect to the registrars view + return $response->withHeader('Location', '/registrars')->withStatus(302); + } + } } \ No newline at end of file diff --git a/cp/app/Controllers/ReportsController.php b/cp/app/Controllers/ReportsController.php index 7590c56..c27bd7a 100644 --- a/cp/app/Controllers/ReportsController.php +++ b/cp/app/Controllers/ReportsController.php @@ -11,6 +11,10 @@ class ReportsController extends Controller { public function view(Request $request, Response $response) { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + return view($response,'admin/reports/index.twig'); } } \ No newline at end of file diff --git a/cp/app/Controllers/UsersController.php b/cp/app/Controllers/UsersController.php index 9d79c83..999ea70 100644 --- a/cp/app/Controllers/UsersController.php +++ b/cp/app/Controllers/UsersController.php @@ -11,6 +11,10 @@ class UsersController extends Controller { public function listUsers(Request $request, Response $response) { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + $userModel = new User($this->container->get('db')); $users = $userModel->getAllUsers(); return view($response,'admin/users/listUsers.twig', compact('users')); diff --git a/cp/resources/views/admin/registrars/updateRegistrarUser.twig b/cp/resources/views/admin/registrars/updateRegistrarUser.twig new file mode 100644 index 0000000..a31d913 --- /dev/null +++ b/cp/resources/views/admin/registrars/updateRegistrarUser.twig @@ -0,0 +1,574 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Update Registrar') }} {{ registrar.name }}{% endblock %} + +{% block content %} +
+ + + +
+
+
+ {% include 'partials/flash.twig' %} +
+ {{ csrf.field | raw }} + +
+
+
{{ __('Registrar Details') }}
+
+ +
+
+ + + {{ __('The official name of the registrar.') }} +
+
+ + + {{ __('Unique identifier assigned by IANA.') }} +
+
+ + + {{ __('Primary contact email of the registrar.') }} +
+
+ + + {{ __('Registrar\'s official website URL.') }} +
+
+ + +
+
+ + + {{ __('Address of the registrar\'s WHOIS server.') }} +
+
+ + + {{ __('Address of the registrar\'s RDAP server.') }} +
+
+ + + {{ __('Email address for reporting abuse.') }} +
+
+ + + {{ __('Phone number for reporting abuse.') }} +
+
+
+
+
+ + +
+
+
{{ __('Financial Information') }}
+
+ +
+
+ +
{{ currency }} {{ registrar.accountBalance }}
+ {{ __('Current balance in the registrar\'s account.') }} +
+
+ +
{{ currency }} {{ registrar.creditLimit }}
+ {{ __('Maximum credit limit for the registrar.') }} +
+
+ + +
+
+ +
{{ currency }} {{ registrar.creditThreshold }}
+ {{ __('Credit threshold triggering alerts or actions.') }} +
+
+ +
{{ registrar.thresholdType|slice(0, 1)|upper ~ registrar.thresholdType|slice(1) }}
+ {{ __('Type of threshold: fixed value or percentage.') }} +
+
+
+
+
+ + +
+
+
{{ __('Registrar Contacts') }}
+ + +
+ +
+
+
+ + +
+
+ {% for contact in contacts %} + {% if contact.type == 'owner' %} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {% endif %} + {% endfor %} +
+
+
+ + +
+
+
+ {% for contact in contacts %} + {% if contact.type == 'billing' %} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {% endif %} + {% endfor %} +
+
+
+ + +
+
+
+ {% for contact in contacts %} + {% if contact.type == 'abuse' %} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ {% endif %} + {% endfor %} +
+
+
+
+
+
+ +
+ +
+
+
+
{{ __('IP Whitelisting') }} *
+

+ {{ __('Whitelist IP addresses for secure access. Up to 5 IP addresses (IPv4 or IPv6) can be added.') }} +

+
+
+ {% for ip in whitelist %} +
+ + +
+ {% endfor %} + + {% if whitelist|length < 5 %} +
+ + +
+ {% endif %} +
+
+
+
+
+ + +
+
+
+
{{ __('Registrar User') }}
+

+ {{ __('For an existing registrar user, you can view the current EPP username (also known as CLID) and panel access email. Additionally, you have the option to reset the passwords for EPP and panel access as needed.') }} +

+
+ + + + + + + + + + + + + + + + + +
{{ __('Username/CLID') }}{{ __('Login Email') }}{{ __('Panel Password') }} *{{ __('EPP Password') }} *
+ {{ registrar.clid }} + + {{ user.email ? user.email : 'N/A' }} + +
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + +
+
+
{{ __('Operational Test and Evaluation (OTE)') }}
+

+ {{ __('Operational Test and Evaluation (OTE) assesses the functionality of EPP commands in a simulated environment, ensuring effective communication between the registrar\'s system and the registry. Below are key EPP commands and their test statuses:') }} +

+
+ +
+
    +
  • + contact:create + {{ __('Pending') }} +
  • +
  • + domain:check + {{ __('Pending') }} +
  • +
  • + domain:info + {{ __('Pending') }} +
  • +
  • + domain:renew + {{ __('Pending') }} +
  • +
  • + domain:transfer + {{ __('Pending') }} +
  • +
+
+ + +
+
    +
  • + host:create + {{ __('Pending') }} +
  • +
  • + host:info + {{ __('Pending') }} +
  • +
  • + contact:update + {{ __('Pending') }} +
  • +
  • + domain:delete + {{ __('Pending') }} +
  • +
  • + poll:request + {{ __('Pending') }} +
  • +
+
+
+
+ +
+
+
+
+
+ {% include 'partials/footer.twig' %} +
+ +{% endblock %} \ No newline at end of file diff --git a/cp/resources/views/admin/registrars/viewRegistrar.twig b/cp/resources/views/admin/registrars/viewRegistrar.twig index d658d4d..5c13dec 100644 --- a/cp/resources/views/admin/registrars/viewRegistrar.twig +++ b/cp/resources/views/admin/registrars/viewRegistrar.twig @@ -27,6 +27,14 @@

{{ __('Registrar') }} {{ registrar.name }} {{ registrar.prefix }} {{ registrar.iana_id|default('N/A') }}

+ {% if roles != 0 %} + + {% endif %}
diff --git a/cp/resources/views/layouts/app.twig b/cp/resources/views/layouts/app.twig index 75a4e37..3aa1039 100644 --- a/cp/resources/views/layouts/app.twig +++ b/cp/resources/views/layouts/app.twig @@ -194,15 +194,18 @@
-
  • +
  • diff --git a/cp/routes/web.php b/cp/routes/web.php index ffc21cc..5ba8642 100644 --- a/cp/routes/web.php +++ b/cp/routes/web.php @@ -90,7 +90,9 @@ $app->group('', function ($route) { $route->get('/registrar/view/{registrar}', RegistrarsController::class . ':viewRegistrar')->setName('viewRegistrar'); $route->get('/registrar/update/{registrar}', RegistrarsController::class . ':updateRegistrar')->setName('updateRegistrar'); $route->post('/registrar/update', RegistrarsController::class . ':updateRegistrarProcess')->setName('updateRegistrarProcess'); - + $route->get('/registrar', RegistrarsController::class .':registrar')->setName('registrar'); + $route->map(['GET', 'POST'], '/registrar/edit', RegistrarsController::class .':editRegistrar')->setName('editRegistrar'); + $route->get('/users', UsersController::class .':listUsers')->setName('listUsers'); $route->get('/epphistory', LogsController::class .':view')->setName('epphistory');