diff --git a/cp/app/Controllers/Auth/AuthController.php b/cp/app/Controllers/Auth/AuthController.php index 9f72ab8..fd3d01a 100644 --- a/cp/app/Controllers/Auth/AuthController.php +++ b/cp/app/Controllers/Auth/AuthController.php @@ -7,6 +7,7 @@ use App\Controllers\Controller; use Respect\Validation\Validator as v; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; +use Pinga\Session; /** * AuthController @@ -59,7 +60,13 @@ class AuthController extends Controller global $container; $data = $request->getParsedBody(); $db = $container->get('db'); - $is2FAEnabled = $db->selectValue('SELECT tfa_enabled, tfa_secret FROM users WHERE email = ?', [$data['email']]); + $is2FAEnabled = $db->selectValue('SELECT tfa_enabled FROM users WHERE email = ?', [$data['email']]); + $isWebaEnabled = $db->selectValue('SELECT auth_method FROM users WHERE email = ?', [$data['email']]); + + if ($isWebaEnabled == 'webauthn') { + $container->get('flash')->addMessage('error', 'WebAuthn enabled for this account'); + return $response->withHeader('Location', '/login')->withStatus(302); + } // If 2FA is enabled and no code is provided, redirect to 2FA code entry if($is2FAEnabled && !isset($data['code'])) { @@ -219,7 +226,7 @@ class AuthController extends Controller // Send success response $user = $db->selectRow('SELECT * FROM users WHERE id = ?', [$user_id]); - Session::regenerate(true); + session_regenerate_id(); $_SESSION['auth_logged_in'] = true; $_SESSION['auth_user_id'] = $user['id']; $_SESSION['auth_email'] = $user['email']; @@ -237,7 +244,7 @@ class AuthController extends Controller 'users_audit', [ 'user_id' => $_SESSION['auth_user_id'], - 'user_event' => 'user.login', + 'user_event' => 'user.login.webauthn', 'user_resource' => 'control.panel', 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'user_ip' => get_client_ip(), diff --git a/cp/app/Controllers/ProfileController.php b/cp/app/Controllers/ProfileController.php index 8798f48..bd25b8c 100644 --- a/cp/app/Controllers/ProfileController.php +++ b/cp/app/Controllers/ProfileController.php @@ -60,19 +60,15 @@ class ProfileController extends Controller [$userId] ); $is_weba_activated = $db->select( - 'SELECT * FROM users_webauthn WHERE user_id = ?', - [$userId] - ); - $user_audit = $db->select( - 'SELECT * FROM users_audit WHERE user_id = ? ORDER BY event_time DESC', + 'SELECT * FROM users_webauthn WHERE user_id = ? ORDER BY created_at DESC LIMIT 5', [$userId] ); if ($is_2fa_activated) { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'userAudit' => $user_audit]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue]); } else if ($is_weba_activated) { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'weba' => $is_weba_activated, 'userAudit' => $user_audit]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'weba' => $is_weba_activated]); } else { - return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'userAudit' => $user_audit]); + return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'qrcodeDataUri' => $qrcodeDataUri, 'secret' => $secret, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue]); } } @@ -203,10 +199,19 @@ class ProfileController extends Controller 'sign_count' => $counter ] ); - - $msg = 'registration success.'; + $db->update( + 'users', + [ + 'auth_method' => 'webauthn' + ], + [ + 'id' => $userId + ] + ); + + $msg = 'Registration success.'; if ($credential->rootValid === false) { - $msg = 'registration ok, but certificate does not match any of the selected root ca.'; + $msg = 'Registration ok, but certificate does not match any of the selected root ca.'; } $return = new \stdClass(); diff --git a/cp/resources/views/admin/profile/profile.twig b/cp/resources/views/admin/profile/profile.twig index 2f62f01..f9c0dad 100644 --- a/cp/resources/views/admin/profile/profile.twig +++ b/cp/resources/views/admin/profile/profile.twig @@ -134,17 +134,23 @@ {% endif %}
+ {% if weba is defined %}

{{ __('WebAuthn Authentication') }}

-

{{ __('Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in.') }}

- +
+ +
+
{{ __('Your account is secured with an additional layer of protection.') }}
+

{{ __('WebAuthn is currently') }} {{ __('enabled') }} {{ __('for your account. If you encounter any issues or need to disable WebAuthn, please contact our support team for assistance.') }}

+
+
+
- @@ -152,9 +158,6 @@ - {% else %} @@ -165,38 +168,20 @@
{{ __('Device/Browser Info') }} {{ __('Registration Date') }}{{ __('Action') }}
{{ device.user_agent }} {{ device.created_at }} - {{ __('Edit') }} -
+ {% else %} +
+

{{ __('WebAuthn Authentication') }}

+

{{ __('Secure your account with WebAuthn. Click the button below to register your device for passwordless sign-in.') }}

+ +
+ {% endif %}

{{ __('User Audit Log') }}

{{ __('Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency.') }}

- - - - - - - - - - - - {% for user in userAudit %} - - - - - - - - {% else %} - - - - {% endfor %} - -
{{ __('Event') }}{{ __('User Agent') }}IP{{ __('Location') }}{{ __('Timestamp') }}
{{ user.user_event }}{{ user.user_agent }}{{ user.user_ip }}{{ user.user_location }}{{ user.event_time }}
{{ __('No log data for user.') }}
+
@@ -205,108 +190,4 @@ {% include 'partials/footer.twig' %} - {% endblock %} \ No newline at end of file diff --git a/cp/resources/views/auth/login.twig b/cp/resources/views/auth/login.twig index 987e0ff..240ee18 100644 --- a/cp/resources/views/auth/login.twig +++ b/cp/resources/views/auth/login.twig @@ -56,20 +56,15 @@ + + + + \ No newline at end of file diff --git a/cp/routes/web.php b/cp/routes/web.php index a9a60ea..124ddd8 100644 --- a/cp/routes/web.php +++ b/cp/routes/web.php @@ -248,13 +248,14 @@ $app->any('/api[/{params:.*}]', function ( 'registrar' => 'id', 'payment_history' => 'registrar_id', 'statement' => 'registrar_id', - 'support_tickets' => 'user_id', // Note: this still uses user_id + 'support_tickets' => 'user_id', // Continues to use user_id + 'users_audit' => 'user_id', // Continues to use user_id ]; if (array_key_exists($tableName, $columnMap)) { // Use registrarId for tables where 'registrar_id' is the filter - // For 'support_tickets', continue to use userId - return [$columnMap[$tableName] => ($tableName === 'support_tickets' ? $_SESSION['auth_user_id'] : $registrarId)]; + // For 'support_tickets' and 'users_audit', use userId + return [$columnMap[$tableName] => (in_array($tableName, ['support_tickets', 'users_audit']) ? $_SESSION['auth_user_id'] : $registrarId)]; } return ['1' => '0']; diff --git a/database/registry.mariadb.sql b/database/registry.mariadb.sql index 02f0cec..e9eebd1 100644 --- a/database/registry.mariadb.sql +++ b/database/registry.mariadb.sql @@ -616,7 +616,8 @@ CREATE TABLE IF NOT EXISTS `registry`.`users_audit` ( `user_data` JSON default NULL, KEY `user_id` (`user_id`), KEY `user_event` (`user_event`), - KEY `user_ip` (`user_ip`) + KEY `user_ip` (`user_ip`), + FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Panel User Audit'; CREATE TABLE IF NOT EXISTS `registry`.`users_confirmations` ( diff --git a/database/registry.postgres.sql b/database/registry.postgres.sql index ddefec7..99e377f 100644 --- a/database/registry.postgres.sql +++ b/database/registry.postgres.sql @@ -877,5 +877,6 @@ ALTER TABLE promotion_pricing ADD FOREIGN KEY ("tld_id") REFERENCES domain_tld(" ALTER TABLE premium_domain_pricing ADD FOREIGN KEY ("tld_id") REFERENCES domain_tld("id"); ALTER TABLE premium_domain_pricing ADD FOREIGN KEY ("category_id") REFERENCES premium_domain_categories("category_id"); ALTER TABLE support_tickets ADD FOREIGN KEY ("user_id") REFERENCES users(id); +ALTER TABLE users_audit ADD FOREIGN KEY ("user_id") REFERENCES users(id); ALTER TABLE support_tickets ADD FOREIGN KEY ("category_id") REFERENCES ticket_categories(id); ALTER TABLE ticket_responses ADD FOREIGN KEY ("ticket_id") REFERENCES support_tickets(id); \ No newline at end of file