SSO Update :)
This commit is contained in:
229
models/ApiKeyModel.php
Normal file
229
models/ApiKeyModel.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* ApiKeyModel - Handles API key generation and validation
|
||||
*/
|
||||
class ApiKeyModel {
|
||||
private $conn;
|
||||
|
||||
public function __construct($conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new API key
|
||||
*
|
||||
* @param string $keyName Descriptive name for the key
|
||||
* @param int $createdBy User ID who created the key
|
||||
* @param int|null $expiresInDays Number of days until expiration (null for no expiration)
|
||||
* @return array Array with 'success', 'api_key' (plaintext), 'key_prefix', 'error'
|
||||
*/
|
||||
public function createKey($keyName, $createdBy, $expiresInDays = null) {
|
||||
// Generate random API key (32 bytes = 64 hex characters)
|
||||
$apiKey = bin2hex(random_bytes(32));
|
||||
|
||||
// Create key prefix (first 8 characters) for identification
|
||||
$keyPrefix = substr($apiKey, 0, 8);
|
||||
|
||||
// Hash the API key for storage
|
||||
$keyHash = hash('sha256', $apiKey);
|
||||
|
||||
// Calculate expiration date if specified
|
||||
$expiresAt = null;
|
||||
if ($expiresInDays !== null) {
|
||||
$expiresAt = date('Y-m-d H:i:s', strtotime("+$expiresInDays days"));
|
||||
}
|
||||
|
||||
// Insert API key into database
|
||||
$stmt = $this->conn->prepare(
|
||||
"INSERT INTO api_keys (key_name, key_hash, key_prefix, created_by, expires_at) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param("sssis", $keyName, $keyHash, $keyPrefix, $createdBy, $expiresAt);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$keyId = $this->conn->insert_id;
|
||||
$stmt->close();
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'api_key' => $apiKey, // Return plaintext key ONCE
|
||||
'key_prefix' => $keyPrefix,
|
||||
'key_id' => $keyId,
|
||||
'expires_at' => $expiresAt
|
||||
];
|
||||
} else {
|
||||
$error = $this->conn->error;
|
||||
$stmt->close();
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $error
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an API key
|
||||
*
|
||||
* @param string $apiKey Plaintext API key to validate
|
||||
* @return array|null API key record if valid, null if invalid
|
||||
*/
|
||||
public function validateKey($apiKey) {
|
||||
if (empty($apiKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hash the provided key
|
||||
$keyHash = hash('sha256', $apiKey);
|
||||
|
||||
// Query for matching key
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1"
|
||||
);
|
||||
$stmt->bind_param("s", $keyHash);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
$stmt->close();
|
||||
return null;
|
||||
}
|
||||
|
||||
$keyData = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
// Check expiration
|
||||
if ($keyData['expires_at'] !== null) {
|
||||
$expiresAt = strtotime($keyData['expires_at']);
|
||||
if ($expiresAt < time()) {
|
||||
return null; // Key has expired
|
||||
}
|
||||
}
|
||||
|
||||
// Update last_used timestamp
|
||||
$this->updateLastUsed($keyData['api_key_id']);
|
||||
|
||||
return $keyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update last_used timestamp for an API key
|
||||
*
|
||||
* @param int $keyId API key ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
private function updateLastUsed($keyId) {
|
||||
$stmt = $this->conn->prepare("UPDATE api_keys SET last_used = NOW() WHERE api_key_id = ?");
|
||||
$stmt->bind_param("i", $keyId);
|
||||
$success = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an API key (set is_active to false)
|
||||
*
|
||||
* @param int $keyId API key ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function revokeKey($keyId) {
|
||||
$stmt = $this->conn->prepare("UPDATE api_keys SET is_active = 0 WHERE api_key_id = ?");
|
||||
$stmt->bind_param("i", $keyId);
|
||||
$success = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an API key permanently
|
||||
*
|
||||
* @param int $keyId API key ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function deleteKey($keyId) {
|
||||
$stmt = $this->conn->prepare("DELETE FROM api_keys WHERE api_key_id = ?");
|
||||
$stmt->bind_param("i", $keyId);
|
||||
$success = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all API keys (for admin panel)
|
||||
*
|
||||
* @return array Array of API key records (without hashes)
|
||||
*/
|
||||
public function getAllKeys() {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT ak.*, u.username, u.display_name
|
||||
FROM api_keys ak
|
||||
LEFT JOIN users u ON ak.created_by = u.user_id
|
||||
ORDER BY ak.created_at DESC"
|
||||
);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$keys = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Remove key_hash from response for security
|
||||
unset($row['key_hash']);
|
||||
$keys[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API key by ID
|
||||
*
|
||||
* @param int $keyId API key ID
|
||||
* @return array|null API key record (without hash) or null if not found
|
||||
*/
|
||||
public function getKeyById($keyId) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT ak.*, u.username, u.display_name
|
||||
FROM api_keys ak
|
||||
LEFT JOIN users u ON ak.created_by = u.user_id
|
||||
WHERE ak.api_key_id = ?"
|
||||
);
|
||||
$stmt->bind_param("i", $keyId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$key = $result->fetch_assoc();
|
||||
// Remove key_hash from response for security
|
||||
unset($key['key_hash']);
|
||||
$stmt->close();
|
||||
return $key;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get keys created by a specific user
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @return array Array of API key records
|
||||
*/
|
||||
public function getKeysByUser($userId) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT * FROM api_keys WHERE created_by = ? ORDER BY created_at DESC"
|
||||
);
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$keys = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Remove key_hash from response for security
|
||||
unset($row['key_hash']);
|
||||
$keys[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
292
models/AuditLogModel.php
Normal file
292
models/AuditLogModel.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/**
|
||||
* AuditLogModel - Handles audit trail logging for all user actions
|
||||
*/
|
||||
class AuditLogModel {
|
||||
private $conn;
|
||||
|
||||
public function __construct($conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an action to the audit trail
|
||||
*
|
||||
* @param int $userId User ID performing the action
|
||||
* @param string $actionType Type of action (e.g., 'create', 'update', 'delete', 'view')
|
||||
* @param string $entityType Type of entity (e.g., 'ticket', 'comment', 'api_key')
|
||||
* @param string|null $entityId ID of the entity affected
|
||||
* @param array|null $details Additional details as associative array
|
||||
* @param string|null $ipAddress IP address of the user
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function log($userId, $actionType, $entityType, $entityId = null, $details = null, $ipAddress = null) {
|
||||
// Convert details array to JSON
|
||||
$detailsJson = null;
|
||||
if ($details !== null) {
|
||||
$detailsJson = json_encode($details);
|
||||
}
|
||||
|
||||
// Get IP address if not provided
|
||||
if ($ipAddress === null) {
|
||||
$ipAddress = $this->getClientIP();
|
||||
}
|
||||
|
||||
$stmt = $this->conn->prepare(
|
||||
"INSERT INTO audit_log (user_id, action_type, entity_type, entity_id, details, ip_address)
|
||||
VALUES (?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->bind_param("isssss", $userId, $actionType, $entityType, $entityId, $detailsJson, $ipAddress);
|
||||
|
||||
$success = $stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit logs for a specific entity
|
||||
*
|
||||
* @param string $entityType Type of entity
|
||||
* @param string $entityId ID of the entity
|
||||
* @param int $limit Maximum number of logs to return
|
||||
* @return array Array of audit log records
|
||||
*/
|
||||
public function getLogsByEntity($entityType, $entityId, $limit = 100) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT al.*, u.username, u.display_name
|
||||
FROM audit_log al
|
||||
LEFT JOIN users u ON al.user_id = u.user_id
|
||||
WHERE al.entity_type = ? AND al.entity_id = ?
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ?"
|
||||
);
|
||||
$stmt->bind_param("ssi", $entityType, $entityId, $limit);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$logs = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Decode JSON details
|
||||
if ($row['details']) {
|
||||
$row['details'] = json_decode($row['details'], true);
|
||||
}
|
||||
$logs[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit logs for a specific user
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @param int $limit Maximum number of logs to return
|
||||
* @return array Array of audit log records
|
||||
*/
|
||||
public function getLogsByUser($userId, $limit = 100) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT al.*, u.username, u.display_name
|
||||
FROM audit_log al
|
||||
LEFT JOIN users u ON al.user_id = u.user_id
|
||||
WHERE al.user_id = ?
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ?"
|
||||
);
|
||||
$stmt->bind_param("ii", $userId, $limit);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$logs = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Decode JSON details
|
||||
if ($row['details']) {
|
||||
$row['details'] = json_decode($row['details'], true);
|
||||
}
|
||||
$logs[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent audit logs (for admin panel)
|
||||
*
|
||||
* @param int $limit Maximum number of logs to return
|
||||
* @param int $offset Offset for pagination
|
||||
* @return array Array of audit log records
|
||||
*/
|
||||
public function getRecentLogs($limit = 50, $offset = 0) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT al.*, u.username, u.display_name
|
||||
FROM audit_log al
|
||||
LEFT JOIN users u ON al.user_id = u.user_id
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ? OFFSET ?"
|
||||
);
|
||||
$stmt->bind_param("ii", $limit, $offset);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$logs = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Decode JSON details
|
||||
if ($row['details']) {
|
||||
$row['details'] = json_decode($row['details'], true);
|
||||
}
|
||||
$logs[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit logs filtered by action type
|
||||
*
|
||||
* @param string $actionType Action type to filter by
|
||||
* @param int $limit Maximum number of logs to return
|
||||
* @return array Array of audit log records
|
||||
*/
|
||||
public function getLogsByAction($actionType, $limit = 100) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"SELECT al.*, u.username, u.display_name
|
||||
FROM audit_log al
|
||||
LEFT JOIN users u ON al.user_id = u.user_id
|
||||
WHERE al.action_type = ?
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ?"
|
||||
);
|
||||
$stmt->bind_param("si", $actionType, $limit);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$logs = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Decode JSON details
|
||||
if ($row['details']) {
|
||||
$row['details'] = json_decode($row['details'], true);
|
||||
}
|
||||
$logs[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total count of audit logs
|
||||
*
|
||||
* @return int Total count
|
||||
*/
|
||||
public function getTotalCount() {
|
||||
$result = $this->conn->query("SELECT COUNT(*) as count FROM audit_log");
|
||||
$row = $result->fetch_assoc();
|
||||
return (int)$row['count'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete old audit logs (for maintenance)
|
||||
*
|
||||
* @param int $daysToKeep Number of days of logs to keep
|
||||
* @return int Number of deleted records
|
||||
*/
|
||||
public function deleteOldLogs($daysToKeep = 90) {
|
||||
$stmt = $this->conn->prepare(
|
||||
"DELETE FROM audit_log WHERE created_at < DATE_SUB(NOW(), INTERVAL ? DAY)"
|
||||
);
|
||||
$stmt->bind_param("i", $daysToKeep);
|
||||
$stmt->execute();
|
||||
$affectedRows = $stmt->affected_rows;
|
||||
$stmt->close();
|
||||
|
||||
return $affectedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client IP address (handles proxies)
|
||||
*
|
||||
* @return string Client IP address
|
||||
*/
|
||||
private function getClientIP() {
|
||||
$ipAddress = '';
|
||||
|
||||
// Check for proxy headers
|
||||
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
|
||||
// Cloudflare
|
||||
$ipAddress = $_SERVER['HTTP_CF_CONNECTING_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
|
||||
// Nginx proxy
|
||||
$ipAddress = $_SERVER['HTTP_X_REAL_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
// Standard proxy header
|
||||
$ipAddress = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
|
||||
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
|
||||
// Direct connection
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return trim($ipAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Log ticket creation
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @param string $ticketId Ticket ID
|
||||
* @param array $ticketData Ticket data
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function logTicketCreate($userId, $ticketId, $ticketData) {
|
||||
return $this->log(
|
||||
$userId,
|
||||
'create',
|
||||
'ticket',
|
||||
$ticketId,
|
||||
['title' => $ticketData['title'], 'priority' => $ticketData['priority'] ?? null]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Log ticket update
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @param string $ticketId Ticket ID
|
||||
* @param array $changes Array of changed fields
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function logTicketUpdate($userId, $ticketId, $changes) {
|
||||
return $this->log($userId, 'update', 'ticket', $ticketId, $changes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Log comment creation
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @param int $commentId Comment ID
|
||||
* @param string $ticketId Associated ticket ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function logCommentCreate($userId, $commentId, $ticketId) {
|
||||
return $this->log(
|
||||
$userId,
|
||||
'create',
|
||||
'comment',
|
||||
(string)$commentId,
|
||||
['ticket_id' => $ticketId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Log ticket view
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @param string $ticketId Ticket ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function logTicketView($userId, $ticketId) {
|
||||
return $this->log($userId, 'view', 'ticket', $ticketId);
|
||||
}
|
||||
}
|
||||
@@ -7,44 +7,58 @@ class CommentModel {
|
||||
}
|
||||
|
||||
public function getCommentsByTicketId($ticketId) {
|
||||
$sql = "SELECT * FROM ticket_comments WHERE ticket_id = ? ORDER BY created_at DESC";
|
||||
$sql = "SELECT tc.*, u.display_name, u.username
|
||||
FROM ticket_comments tc
|
||||
LEFT JOIN users u ON tc.user_id = u.user_id
|
||||
WHERE tc.ticket_id = ?
|
||||
ORDER BY tc.created_at DESC";
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->bind_param("s", $ticketId); // Changed to string since ticket_id is varchar
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
|
||||
$comments = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
// Use display_name from users table if available, fallback to user_name field
|
||||
if (!empty($row['display_name'])) {
|
||||
$row['display_name_formatted'] = $row['display_name'];
|
||||
} else {
|
||||
$row['display_name_formatted'] = $row['user_name'] ?? 'Unknown User';
|
||||
}
|
||||
$comments[] = $row;
|
||||
}
|
||||
|
||||
|
||||
return $comments;
|
||||
}
|
||||
|
||||
public function addComment($ticketId, $commentData) {
|
||||
$sql = "INSERT INTO ticket_comments (ticket_id, user_name, comment_text, markdown_enabled)
|
||||
VALUES (?, ?, ?, ?)";
|
||||
|
||||
public function addComment($ticketId, $commentData, $userId = null) {
|
||||
$sql = "INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled)
|
||||
VALUES (?, ?, ?, ?, ?)";
|
||||
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
|
||||
// Set default username
|
||||
|
||||
// Set default username (kept for backward compatibility)
|
||||
$username = $commentData['user_name'] ?? 'User';
|
||||
$markdownEnabled = isset($commentData['markdown_enabled']) && $commentData['markdown_enabled'] ? 1 : 0;
|
||||
|
||||
|
||||
// Preserve line breaks in the comment text
|
||||
$commentText = $commentData['comment_text'];
|
||||
|
||||
|
||||
$stmt->bind_param(
|
||||
"sssi",
|
||||
"sissi",
|
||||
$ticketId,
|
||||
$userId,
|
||||
$username,
|
||||
$commentText,
|
||||
$markdownEnabled
|
||||
);
|
||||
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$commentId = $this->conn->insert_id;
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'comment_id' => $commentId,
|
||||
'user_name' => $username,
|
||||
'created_at' => date('M d, Y H:i'),
|
||||
'markdown_enabled' => $markdownEnabled,
|
||||
|
||||
@@ -134,7 +134,7 @@ class TicketModel {
|
||||
];
|
||||
}
|
||||
|
||||
public function updateTicket($ticketData) {
|
||||
public function updateTicket($ticketData, $updatedBy = null) {
|
||||
// Debug function
|
||||
$debug = function($message, $data = null) {
|
||||
$log_message = date('Y-m-d H:i:s') . " - [Model] " . $message;
|
||||
@@ -146,46 +146,48 @@ class TicketModel {
|
||||
};
|
||||
|
||||
$debug("updateTicket called with data", $ticketData);
|
||||
|
||||
$sql = "UPDATE tickets SET
|
||||
title = ?,
|
||||
priority = ?,
|
||||
status = ?,
|
||||
|
||||
$sql = "UPDATE tickets SET
|
||||
title = ?,
|
||||
priority = ?,
|
||||
status = ?,
|
||||
description = ?,
|
||||
category = ?,
|
||||
type = ?,
|
||||
updated_at = NOW()
|
||||
updated_by = ?,
|
||||
updated_at = NOW()
|
||||
WHERE ticket_id = ?";
|
||||
|
||||
|
||||
$debug("SQL query", $sql);
|
||||
|
||||
|
||||
try {
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
if (!$stmt) {
|
||||
$debug("Prepare statement failed", $this->conn->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$debug("Binding parameters");
|
||||
$stmt->bind_param(
|
||||
"sissssi",
|
||||
"siisssii",
|
||||
$ticketData['title'],
|
||||
$ticketData['priority'],
|
||||
$ticketData['status'],
|
||||
$ticketData['description'],
|
||||
$ticketData['category'],
|
||||
$ticketData['type'],
|
||||
$updatedBy,
|
||||
$ticketData['ticket_id']
|
||||
);
|
||||
|
||||
|
||||
$debug("Executing statement");
|
||||
$result = $stmt->execute();
|
||||
|
||||
|
||||
if (!$result) {
|
||||
$debug("Execute failed", $stmt->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$debug("Update successful");
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
@@ -195,32 +197,33 @@ class TicketModel {
|
||||
}
|
||||
}
|
||||
|
||||
public function createTicket($ticketData) {
|
||||
public function createTicket($ticketData, $createdBy = null) {
|
||||
// Generate ticket ID (9-digit format with leading zeros)
|
||||
$ticket_id = sprintf('%09d', mt_rand(1, 999999999));
|
||||
|
||||
$sql = "INSERT INTO tickets (ticket_id, title, description, status, priority, category, type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
|
||||
$sql = "INSERT INTO tickets (ticket_id, title, description, status, priority, category, type, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
|
||||
|
||||
// Set default values if not provided
|
||||
$status = $ticketData['status'] ?? 'Open';
|
||||
$priority = $ticketData['priority'] ?? '4';
|
||||
$category = $ticketData['category'] ?? 'General';
|
||||
$type = $ticketData['type'] ?? 'Issue';
|
||||
|
||||
|
||||
$stmt->bind_param(
|
||||
"sssssss",
|
||||
"sssssssi",
|
||||
$ticket_id,
|
||||
$ticketData['title'],
|
||||
$ticketData['description'],
|
||||
$status,
|
||||
$priority,
|
||||
$category,
|
||||
$type
|
||||
$type,
|
||||
$createdBy
|
||||
);
|
||||
|
||||
|
||||
if ($stmt->execute()) {
|
||||
return [
|
||||
'success' => true,
|
||||
|
||||
227
models/UserModel.php
Normal file
227
models/UserModel.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/**
|
||||
* UserModel - Handles user authentication and management
|
||||
*/
|
||||
class UserModel {
|
||||
private $conn;
|
||||
private static $userCache = [];
|
||||
|
||||
public function __construct($conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync user from Authelia headers (create or update)
|
||||
*
|
||||
* @param string $username Username from Remote-User header
|
||||
* @param string $displayName Display name from Remote-Name header
|
||||
* @param string $email Email from Remote-Email header
|
||||
* @param string $groups Comma-separated groups from Remote-Groups header
|
||||
* @return array User data array
|
||||
*/
|
||||
public function syncUserFromAuthelia($username, $displayName = '', $email = '', $groups = '') {
|
||||
// Check cache first
|
||||
$cacheKey = "user_$username";
|
||||
if (isset(self::$userCache[$cacheKey])) {
|
||||
return self::$userCache[$cacheKey];
|
||||
}
|
||||
|
||||
// Determine if user is admin based on groups
|
||||
$isAdmin = $this->checkAdminStatus($groups);
|
||||
|
||||
// Try to find existing user
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->bind_param("s", $username);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
// Update existing user
|
||||
$user = $result->fetch_assoc();
|
||||
|
||||
$updateStmt = $this->conn->prepare(
|
||||
"UPDATE users SET display_name = ?, email = ?, groups = ?, is_admin = ?, last_login = NOW() WHERE username = ?"
|
||||
);
|
||||
$updateStmt->bind_param("sssis", $displayName, $email, $groups, $isAdmin, $username);
|
||||
$updateStmt->execute();
|
||||
$updateStmt->close();
|
||||
|
||||
// Refresh user data
|
||||
$user['display_name'] = $displayName;
|
||||
$user['email'] = $email;
|
||||
$user['groups'] = $groups;
|
||||
$user['is_admin'] = $isAdmin;
|
||||
} else {
|
||||
// Create new user
|
||||
$insertStmt = $this->conn->prepare(
|
||||
"INSERT INTO users (username, display_name, email, groups, is_admin, last_login) VALUES (?, ?, ?, ?, ?, NOW())"
|
||||
);
|
||||
$insertStmt->bind_param("ssssi", $username, $displayName, $email, $groups, $isAdmin);
|
||||
$insertStmt->execute();
|
||||
|
||||
$userId = $this->conn->insert_id;
|
||||
$insertStmt->close();
|
||||
|
||||
// Get the newly created user
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE user_id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$user = $result->fetch_assoc();
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
|
||||
// Cache user
|
||||
self::$userCache[$cacheKey] = $user;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system user (for hwmonDaemon)
|
||||
*
|
||||
* @return array|null System user data or null if not found
|
||||
*/
|
||||
public function getSystemUser() {
|
||||
// Check cache first
|
||||
if (isset(self::$userCache['system'])) {
|
||||
return self::$userCache['system'];
|
||||
}
|
||||
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = 'system'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$user = $result->fetch_assoc();
|
||||
self::$userCache['system'] = $user;
|
||||
$stmt->close();
|
||||
return $user;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user by ID
|
||||
*
|
||||
* @param int $userId User ID
|
||||
* @return array|null User data or null if not found
|
||||
*/
|
||||
public function getUserById($userId) {
|
||||
// Check cache first
|
||||
$cacheKey = "user_id_$userId";
|
||||
if (isset(self::$userCache[$cacheKey])) {
|
||||
return self::$userCache[$cacheKey];
|
||||
}
|
||||
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE user_id = ?");
|
||||
$stmt->bind_param("i", $userId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$user = $result->fetch_assoc();
|
||||
self::$userCache[$cacheKey] = $user;
|
||||
$stmt->close();
|
||||
return $user;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user by username
|
||||
*
|
||||
* @param string $username Username
|
||||
* @return array|null User data or null if not found
|
||||
*/
|
||||
public function getUserByUsername($username) {
|
||||
// Check cache first
|
||||
$cacheKey = "user_$username";
|
||||
if (isset(self::$userCache[$cacheKey])) {
|
||||
return self::$userCache[$cacheKey];
|
||||
}
|
||||
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->bind_param("s", $username);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$user = $result->fetch_assoc();
|
||||
self::$userCache[$cacheKey] = $user;
|
||||
$stmt->close();
|
||||
return $user;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has admin privileges based on groups
|
||||
*
|
||||
* @param string $groups Comma-separated group names
|
||||
* @return bool True if user is in admin group
|
||||
*/
|
||||
private function checkAdminStatus($groups) {
|
||||
if (empty($groups)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Split groups by comma and check for 'admin' group
|
||||
$groupArray = array_map('trim', explode(',', strtolower($groups)));
|
||||
return in_array('admin', $groupArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is admin
|
||||
*
|
||||
* @param array $user User data array
|
||||
* @return bool True if user is admin
|
||||
*/
|
||||
public function isAdmin($user) {
|
||||
return isset($user['is_admin']) && $user['is_admin'] == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has required group membership
|
||||
*
|
||||
* @param array $user User data array
|
||||
* @param array $requiredGroups Array of required group names
|
||||
* @return bool True if user is in at least one required group
|
||||
*/
|
||||
public function hasGroupAccess($user, $requiredGroups = ['admin', 'employee']) {
|
||||
if (empty($user['groups'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userGroups = array_map('trim', explode(',', strtolower($user['groups'])));
|
||||
$requiredGroups = array_map('strtolower', $requiredGroups);
|
||||
|
||||
return !empty(array_intersect($userGroups, $requiredGroups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users (for admin panel)
|
||||
*
|
||||
* @return array Array of user records
|
||||
*/
|
||||
public function getAllUsers() {
|
||||
$stmt = $this->conn->prepare("SELECT * FROM users ORDER BY created_at DESC");
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$users = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$users[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return $users;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user