diff --git a/add-client.php b/add-client.php
new file mode 100644
index 0000000..3f5c949
--- /dev/null
+++ b/add-client.php
@@ -0,0 +1,136 @@
+prepare("SELECT COUNT(*) FROM clients WHERE client_id = ?");
+ $stmt->execute([$clientId]);
+ if ($stmt->fetchColumn() > 0) {
+ $errors[] = "Client ID already exists.";
+ } else {
+ // Insert into database
+ $stmt = $pdo->prepare("INSERT INTO clients (client_id, name, status) VALUES (?, ?, ?)");
+ if ($stmt->execute([$clientId, $clientName, $status])) {
+ log_audit_event('client_create', $_SESSION['user_id'], 'client', $clientId);
+ header("Location: dashboard.php?status=client_added");
+ exit;
+ } else {
+ $errors[] = "Failed to create the client. Please try again.";
+ }
+ }
+ }
+}
+?>
+
+
+
+
+
"${newNote.note.replace(/\n/g, '
')}"
+
+ Added by ${newNote.display_name} on ${new Date(newNote.created_at).toLocaleString()}
+
+
+
+ `;
+
+ // Add the new note to the list
+ const notesList = document.getElementById('notes-list');
+ notesList.insertBefore(noteElement, notesList.firstChild);
+
+ // Hide the "no notes" message if it's visible
+ const noNotesMessage = document.getElementById('no-notes-message');
+ if (noNotesMessage) {
+ noNotesMessage.classList.add('d-none');
+ }
+
+ // Clear the textarea
+ noteTextarea.value = '';
+ } else {
+ alert('Error adding note: ' + data.message);
+ }
+ })
+ .catch(error => {
+ console.error('Error:', error);
+ alert('An unexpected error occurred.');
+ });
+ });
+ }
+
+ // AJAX for deleting a new note
+ const notesList = document.getElementById('notes-list');
+ if (notesList) {
+ notesList.addEventListener('click', function (e) {
+ const deleteButton = e.target.closest('.delete-note-btn');
+ if (deleteButton) {
+ e.preventDefault();
+
+ if (confirm('Are you sure you want to delete this note?')) {
+ const noteId = deleteButton.dataset.noteId;
+
+ fetch(`delete-note.php?note_id=${noteId}`)
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ // Remove the note element from the DOM
+ deleteButton.closest('.card').remove();
+
+ // Show the "no notes" message if the list is empty
+ if (notesList.children.length === 0) {
+ const noNotesMessage = document.getElementById('no-notes-message');
+ if (noNotesMessage) {
+ noNotesMessage.classList.remove('d-none');
+ }
+ }
+ } else {
+ alert('Error deleting note: ' + data.message);
+ }
+ })
+ .catch(error => {
+ console.error('Error:', error);
+ alert('An unexpected error occurred.');
+ });
+ }
+ }
+ });
+ }
+});
diff --git a/audit-log.php b/audit-log.php
new file mode 100644
index 0000000..d7dd1cd
--- /dev/null
+++ b/audit-log.php
@@ -0,0 +1,114 @@
+query(
+ "SELECT ae.*, u.display_name " .
+ "FROM audit_events ae " .
+ "LEFT JOIN users u ON ae.user_id = u.id " .
+ "ORDER BY ae.created_at DESC LIMIT 200" // Limit to recent 200 events for performance
+);
+$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+?>
+
+
+
+
@@ -72,9 +90,44 @@ if (isset($_GET['client_id'])) {
+
+
+ New credential has been added successfully.
+
+
+
+
+
+
+ Credential has been updated successfully.
+
+
+
+
+
+
+ Credential has been deleted successfully.
+
+
+
+
+
+
+ New note has been added successfully.
+
+
+
+
+
+
+ Note has been deleted successfully.
+
+
+
+
@@ -103,9 +223,19 @@ if (isset($_GET['client_id'])) {
Clients
-
+
Add New Client
+
+
+ New client has been added successfully.
+
+
+
+
+
+
+
@@ -117,7 +247,7 @@ if (isset($_GET['client_id'])) {
| Actions |
-
+
|
diff --git a/delete-credential.php b/delete-credential.php
new file mode 100644
index 0000000..02ca8a1
--- /dev/null
+++ b/delete-credential.php
@@ -0,0 +1,42 @@
+prepare("SELECT client_id FROM credentials WHERE credential_id = ?");
+$stmt->execute([$credential_id]);
+$credential = $stmt->fetch(PDO::FETCH_ASSOC);
+
+if ($credential) {
+ $client_id = $credential['client_id'];
+
+ log_audit_event('credential_delete', $_SESSION['user_id'], 'credential', $credential_id);
+
+ // Delete the credential
+ $deleteStmt = $pdo->prepare("DELETE FROM credentials WHERE credential_id = ?");
+ $deleteStmt->execute([$credential_id]);
+
+ // Redirect back to the client detail page with a success message
+ header("Location: dashboard.php?client_id=$client_id&status=credential_deleted");
+ exit;
+} else {
+ // Credential not found, just redirect
+ header('Location: dashboard.php');
+ exit;
+}
diff --git a/delete-note.php b/delete-note.php
new file mode 100644
index 0000000..bdfdee2
--- /dev/null
+++ b/delete-note.php
@@ -0,0 +1,50 @@
+ false, 'message' => 'User not logged in.']);
+ exit;
+}
+
+$noteId = $_GET['note_id'] ?? null;
+
+if ($noteId) {
+ try {
+ $pdo = db();
+
+ // First, get the client_id for redirection
+ $stmt = $pdo->prepare("SELECT client_id FROM notes WHERE note_id = ?");
+ $stmt->execute([$noteId]);
+ $note = $stmt->fetch(PDO::FETCH_ASSOC);
+ $clientId = $note['client_id'] ?? null;
+
+ if ($clientId) {
+ log_audit_event('note_delete', $_SESSION['user_id'], 'note', $noteId);
+
+ // Now, delete the note
+ $deleteStmt = $pdo->prepare("DELETE FROM notes WHERE note_id = ?");
+ $deleteStmt->execute([$noteId]);
+
+ echo json_encode(['success' => true]);
+ exit;
+ } else {
+ // Note not found or no client_id associated
+ echo json_encode(['success' => false, 'message' => 'Note not found.']);
+ exit;
+ }
+ } catch (PDOException $e) {
+ // Optional: Log error
+ echo json_encode(['success' => false, 'message' => 'Database error.']);
+ exit;
+ }
+} else {
+ // Redirect if note_id is missing
+ echo json_encode(['success' => false, 'message' => 'Note ID is required.']);
+ exit;
+}
+?>
\ No newline at end of file
diff --git a/edit-credential.php b/edit-credential.php
new file mode 100644
index 0000000..f3736d7
--- /dev/null
+++ b/edit-credential.php
@@ -0,0 +1,158 @@
+prepare("SELECT * FROM credentials WHERE credential_id = ?");
+$stmt->execute([$credential_id]);
+$credential = $stmt->fetch(PDO::FETCH_ASSOC);
+
+if (!$credential) {
+ // Credential not found
+ header('Location: dashboard.php');
+ exit;
+}
+
+$client_id = $credential['client_id']; // For redirecting back
+
+// Handle form submission
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $name = trim($_POST['name']);
+ $username = trim($_POST['username']);
+ $password = $_POST['password']; // Don't trim password
+ $url = trim($_POST['url']);
+ $notes = trim($_POST['notes']);
+
+ // Basic validation
+ if (empty($name) || empty($username)) {
+ $error = "Credential Name and Username are required.";
+ } else {
+ // If password is not changed, don't update it
+ if (empty($password)) {
+ $updateStmt = $pdo->prepare(
+ "UPDATE credentials SET name = ?, username = ?, url = ?, notes = ?, updated_at = NOW() WHERE credential_id = ?"
+ );
+ $updateStmt->execute([$name, $username, $url, $notes, $credential_id]);
+ } else {
+ $updateStmt = $pdo->prepare(
+ "UPDATE credentials SET name = ?, username = ?, password = ?, url = ?, notes = ?, updated_at = NOW() WHERE credential_id = ?"
+ );
+ $updateStmt->execute([$name, $username, $password, $url, $notes, $credential_id]);
+ }
+
+ log_audit_event('credential_update', $_SESSION['user_id'], 'credential', $credential_id);
+
+ // Redirect back to the client detail page with a success message
+ header("Location: dashboard.php?client_id=$client_id&status=credential_updated");
+ exit;
+ }
+}
+
+$displayName = $_SESSION['user_display_name'] ?? 'User';
+
+?>
+
+
+
+
+
+ Edit Credential - FlexPass
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/includes/audit.php b/includes/audit.php
new file mode 100644
index 0000000..cd14fd5
--- /dev/null
+++ b/includes/audit.php
@@ -0,0 +1,26 @@
+prepare(
+ "INSERT INTO audit_events (user_id, action, target_type, target_id) VALUES (?, ?, ?, ?)"
+ );
+ $stmt->execute([$userId, $action, $targetType, $targetId]);
+ } catch (PDOException $e) {
+ // In a real application, you would log this error to a file or monitoring service.
+ // For this example, we'll fail silently to not disrupt the user experience.
+ error_log('Audit log failed: ' . $e->getMessage());
+ }
+}
+?>
\ No newline at end of file
diff --git a/index.php b/index.php
index 03e5544..62f6583 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,7 @@
prepare("UPDATE users SET last_login_at = CURRENT_TIMESTAMP WHERE id = ?");
$updateStmt->execute([$user['id']]);
+ log_audit_event('login_success', $user['id']);
+
header('Location: dashboard.php');
exit;
} else {
$error_message = 'Your account is disabled. Please contact an administrator.';
+ log_audit_event('login_failed_disabled', $user['id']);
}
} else {
$error_message = 'Invalid email or password.';
+ log_audit_event('login_failed_credentials', $user['id'] ?? null);
}
} catch (PDOException $e) {
// In a real app, you would log this error.
diff --git a/logout.php b/logout.php
index d021a83..cfc9635 100644
--- a/logout.php
+++ b/logout.php
@@ -1,5 +1,10 @@