mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-13 16:16:59 +02:00
Added user audit details in panel
This commit is contained in:
parent
2c48d48777
commit
6d1f934d36
8 changed files with 197 additions and 25 deletions
|
@ -204,6 +204,10 @@
|
|||
"audit": true,
|
||||
"skip": null
|
||||
},
|
||||
"users_audit": {
|
||||
"audit": true,
|
||||
"skip": null
|
||||
},
|
||||
"users_confirmations": {
|
||||
"audit": null,
|
||||
"skip": null
|
||||
|
|
|
@ -277,6 +277,23 @@ class Auth
|
|||
public static function changeCurrentPassword($oldPassword, $newPassword){
|
||||
$auth = self::$auth;
|
||||
try {
|
||||
global $container;
|
||||
$db = $container->get('db');
|
||||
$currentDateTime = new \DateTime();
|
||||
$currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
|
||||
$db->insert(
|
||||
'users_audit',
|
||||
[
|
||||
'user_id' => $_SESSION['auth_user_id'],
|
||||
'user_event' => 'user.update.password',
|
||||
'user_resource' => 'control.panel',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
|
||||
'user_ip' => get_client_ip(),
|
||||
'user_location' => get_client_location(),
|
||||
'event_time' => $currentDate,
|
||||
'user_data' => null
|
||||
]
|
||||
);
|
||||
$auth->changePassword($oldPassword, $newPassword);
|
||||
redirect()->route('profile')->with('success','Password has been changed');
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ class AuthController extends Controller
|
|||
* @throws \Pinga\Auth\AuthError
|
||||
*/
|
||||
public function login(Request $request, Response $response){
|
||||
global $container;
|
||||
|
||||
$data = $request->getParsedBody();
|
||||
if(isset($data['remember'])){
|
||||
$remember = $data['remember'];
|
||||
|
@ -71,8 +73,25 @@ class AuthController extends Controller
|
|||
$code = null;
|
||||
}
|
||||
$login = Auth::login($data['email'], $data['password'], $remember, $code);
|
||||
if($login===true)
|
||||
if($login===true) {
|
||||
$db = $container->get('db');
|
||||
$currentDateTime = new \DateTime();
|
||||
$currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
|
||||
$db->insert(
|
||||
'users_audit',
|
||||
[
|
||||
'user_id' => $_SESSION['auth_user_id'],
|
||||
'user_event' => 'user.login',
|
||||
'user_resource' => 'control.panel',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
|
||||
'user_ip' => get_client_ip(),
|
||||
'user_location' => get_client_location(),
|
||||
'event_time' => $currentDate,
|
||||
'user_data' => null
|
||||
]
|
||||
);
|
||||
redirect()->route('home');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,6 +99,23 @@ class AuthController extends Controller
|
|||
*/
|
||||
public function logout()
|
||||
{
|
||||
global $container;
|
||||
$db = $container->get('db');
|
||||
$currentDateTime = new \DateTime();
|
||||
$currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
|
||||
$db->insert(
|
||||
'users_audit',
|
||||
[
|
||||
'user_id' => $_SESSION['auth_user_id'],
|
||||
'user_event' => 'user.logout',
|
||||
'user_resource' => 'control.panel',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
|
||||
'user_ip' => get_client_ip(),
|
||||
'user_location' => get_client_location(),
|
||||
'event_time' => $currentDate,
|
||||
'user_data' => null
|
||||
]
|
||||
);
|
||||
Auth::logout();
|
||||
redirect()->route('login');
|
||||
}
|
||||
|
|
|
@ -73,12 +73,16 @@ class ProfileController extends Controller
|
|||
'SELECT * FROM users_webauthn WHERE user_id = ?',
|
||||
[$userId]
|
||||
);
|
||||
$user_audit = $db->select(
|
||||
'SELECT * FROM users_audit WHERE user_id = ? ORDER BY event_time DESC',
|
||||
[$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]);
|
||||
return view($response,'admin/profile/profile.twig',['email' => $email, 'username' => $username, 'status' => $status, 'role' => $role, 'csrf_name' => $csrfName, 'csrf_value' => $csrfValue, 'userAudit' => $user_audit]);
|
||||
} 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]);
|
||||
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]);
|
||||
} 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]);
|
||||
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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -114,6 +118,21 @@ class ProfileController extends Controller
|
|||
}
|
||||
|
||||
try {
|
||||
$currentDateTime = new \DateTime();
|
||||
$currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
|
||||
$db->insert(
|
||||
'users_audit',
|
||||
[
|
||||
'user_id' => $_SESSION['auth_user_id'],
|
||||
'user_event' => 'user.enable.2fa',
|
||||
'user_resource' => 'control.panel',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
|
||||
'user_ip' => get_client_ip(),
|
||||
'user_location' => get_client_location(),
|
||||
'event_time' => $currentDate,
|
||||
'user_data' => null
|
||||
]
|
||||
);
|
||||
$db->update(
|
||||
'users',
|
||||
[
|
||||
|
|
|
@ -397,3 +397,32 @@ function createUuidFromId($id) {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get the client IP address
|
||||
function get_client_ip() {
|
||||
$ipaddress = '';
|
||||
if (getenv('HTTP_CLIENT_IP'))
|
||||
$ipaddress = getenv('HTTP_CLIENT_IP');
|
||||
else if(getenv('HTTP_X_FORWARDED_FOR'))
|
||||
$ipaddress = getenv('HTTP_X_FORWARDED_FOR');
|
||||
else if(getenv('HTTP_X_FORWARDED'))
|
||||
$ipaddress = getenv('HTTP_X_FORWARDED');
|
||||
else if(getenv('HTTP_FORWARDED_FOR'))
|
||||
$ipaddress = getenv('HTTP_FORWARDED_FOR');
|
||||
else if(getenv('HTTP_FORWARDED'))
|
||||
$ipaddress = getenv('HTTP_FORWARDED');
|
||||
else if(getenv('REMOTE_ADDR'))
|
||||
$ipaddress = getenv('REMOTE_ADDR');
|
||||
else
|
||||
$ipaddress = 'UNKNOWN';
|
||||
return $ipaddress;
|
||||
}
|
||||
|
||||
function get_client_location() {
|
||||
$PublicIP = get_client_ip();
|
||||
$json = file_get_contents("http://ipinfo.io/$PublicIP/geo");
|
||||
$json = json_decode($json, true);
|
||||
$country = $json['country'];
|
||||
|
||||
return $country;
|
||||
}
|
|
@ -36,6 +36,9 @@
|
|||
<li class="nav-item">
|
||||
<a href="#tabs-webauthn" class="nav-link" data-bs-toggle="tab">WebAuthn</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#tabs-log" class="nav-link" data-bs-toggle="tab">Log</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
@ -158,19 +161,55 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for device in weba %}
|
||||
<tr>
|
||||
<td>{{ device.user_agent }}</td>
|
||||
<td>{{ device.created_at }}</td>
|
||||
<td>
|
||||
<a href="/path/to/action?deviceId={{ device.id }}">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="3">No devices found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for device in weba %}
|
||||
<tr>
|
||||
<td>{{ device.user_agent }}</td>
|
||||
<td>{{ device.created_at }}</td>
|
||||
<td>
|
||||
<a href="/path/to/action?deviceId={{ device.id }}">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="3">No devices found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-log">
|
||||
<h4 class="card-title">User Audit Log</h4>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency.</p>
|
||||
<div class="table-responsive mt-4">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event</th>
|
||||
<th>User Agent</th>
|
||||
<th>IP</th>
|
||||
<th>Location</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in userAudit %}
|
||||
<tr>
|
||||
<td>{{ user.user_event }}</td>
|
||||
<td>{{ user.user_agent }}</td>
|
||||
<td>{{ user.user_ip }}</td>
|
||||
<td>{{ user.user_location }}</td>
|
||||
<td>{{ user.event_time }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5">No log data for user.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -601,6 +601,20 @@ CREATE TABLE IF NOT EXISTS `registry`.`users` (
|
|||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Panel Users';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `registry`.`users_audit` (
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`user_event` VARCHAR(255) NOT NULL,
|
||||
`user_resource` VARCHAR(255) default NULL,
|
||||
`user_agent` VARCHAR(255) NOT NULL,
|
||||
`user_ip` VARCHAR(45) NOT NULL,
|
||||
`user_location` VARCHAR(45) default NULL,
|
||||
`event_time` DATETIME(3) NOT NULL,
|
||||
`user_data` JSON default NULL,
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `user_event` (`user_event`),
|
||||
KEY `user_ip` (`user_ip`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Panel User Audit';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `registry`.`users_confirmations` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
|
@ -616,24 +630,24 @@ CREATE TABLE IF NOT EXISTS `registry`.`users_confirmations` (
|
|||
|
||||
CREATE TABLE IF NOT EXISTS `registry`.`users_remembered` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`selector` varchar(24) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`expires` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `selector` (`selector`),
|
||||
KEY `user` (`user`)
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Panel Users Remember';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `registry`.`users_resets` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`selector` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
||||
`expires` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `selector` (`selector`),
|
||||
KEY `user_expires` (`user`,`expires`)
|
||||
KEY `user_expires` (`user_id`,`expires`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Panel Users Reset';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `registry`.`users_throttling` (
|
||||
|
|
|
@ -573,6 +573,20 @@ CREATE TABLE IF NOT EXISTS registry.users (
|
|||
"backup_codes" TEXT,
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS registry.users_audit (
|
||||
"user_id" SERIAL PRIMARY KEY CHECK ("id" >= 0),
|
||||
"user_event" VARCHAR(255) NOT NULL,
|
||||
"user_resource" VARCHAR(255) DEFAULT NULL,
|
||||
"user_agent" VARCHAR(255) NOT NULL,
|
||||
"user_ip" VARCHAR(45) NOT NULL,
|
||||
"user_location" VARCHAR(45) DEFAULT NULL,
|
||||
"event_time" TIMESTAMP(3) NOT NULL,
|
||||
"user_data" JSONB DEFAULT NULL,
|
||||
CONSTRAINT pk_users_audit PRIMARY KEY (user_id)
|
||||
);
|
||||
CREATE INDEX idx_user_event ON registry.users_audit (user_event);
|
||||
CREATE INDEX idx_user_ip ON registry.users_audit (user_ip);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS registry.users_confirmations (
|
||||
"id" SERIAL PRIMARY KEY CHECK ("id" >= 0),
|
||||
"user_id" INTEGER NOT NULL CHECK ("user_id" >= 0),
|
||||
|
@ -586,21 +600,21 @@ CREATE INDEX IF NOT EXISTS "user_id" ON registry.users_confirmations ("user_id")
|
|||
|
||||
CREATE TABLE IF NOT EXISTS registry.users_remembered (
|
||||
"id" BIGSERIAL PRIMARY KEY CHECK ("id" >= 0),
|
||||
"user" INTEGER NOT NULL CHECK ("user" >= 0),
|
||||
"user_id" INTEGER NOT NULL CHECK ("user_id" >= 0),
|
||||
"selector" VARCHAR(24) UNIQUE NOT NULL,
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"expires" INTEGER NOT NULL CHECK ("expires" >= 0)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS "user" ON registry.users_remembered ("user");
|
||||
CREATE INDEX IF NOT EXISTS "user_id" ON registry.users_remembered ("user_id");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS registry.users_resets (
|
||||
"id" BIGSERIAL PRIMARY KEY CHECK ("id" >= 0),
|
||||
"user" INTEGER NOT NULL CHECK ("user" >= 0),
|
||||
"user_id" INTEGER NOT NULL CHECK ("user_id" >= 0),
|
||||
"selector" VARCHAR(20) UNIQUE NOT NULL,
|
||||
"token" VARCHAR(255) NOT NULL,
|
||||
"expires" INTEGER NOT NULL CHECK ("expires" >= 0)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS "user_expires" ON registry.users_resets ("user", "expires");
|
||||
CREATE INDEX IF NOT EXISTS "user_expires" ON registry.users_resets ("user_id", "expires");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS registry.users_throttling (
|
||||
"bucket" VARCHAR(44) PRIMARY KEY,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue