2026-01-01 15:40:32 -05:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* UserModel - Handles user authentication and management
|
|
|
|
|
*/
|
|
|
|
|
class UserModel {
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
private mysqli $conn;
|
|
|
|
|
private static array $userCache = []; // ['key' => ['data' => ..., 'expires' => timestamp]]
|
|
|
|
|
private static int $cacheTTL = 300; // 5 minutes
|
2026-01-01 15:40:32 -05:00
|
|
|
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function __construct(mysqli $conn) {
|
2026-01-01 15:40:32 -05:00
|
|
|
$this->conn = $conn;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 16:27:04 -05:00
|
|
|
/**
|
|
|
|
|
* Get cached user data if not expired
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
private static function getCached(string $key): ?array {
|
2026-01-09 16:27:04 -05:00
|
|
|
if (isset(self::$userCache[$key])) {
|
|
|
|
|
$cached = self::$userCache[$key];
|
|
|
|
|
if ($cached['expires'] > time()) {
|
|
|
|
|
return $cached['data'];
|
|
|
|
|
}
|
|
|
|
|
// Expired - remove from cache
|
|
|
|
|
unset(self::$userCache[$key]);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store user data in cache with expiration
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
private static function setCached(string $key, array $data): void {
|
2026-01-09 16:27:04 -05:00
|
|
|
self::$userCache[$key] = [
|
|
|
|
|
'data' => $data,
|
|
|
|
|
'expires' => time() + self::$cacheTTL
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invalidate specific user cache entry
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public static function invalidateCache(?int $userId = null, ?string $username = null): void {
|
2026-01-09 16:27:04 -05:00
|
|
|
if ($userId !== null) {
|
|
|
|
|
unset(self::$userCache["user_id_$userId"]);
|
|
|
|
|
}
|
|
|
|
|
if ($username !== null) {
|
|
|
|
|
unset(self::$userCache["user_$username"]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:40:32 -05:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function syncUserFromAuthelia(string $username, string $displayName = '', string $email = '', string $groups = ''): array {
|
2026-01-01 15:40:32 -05:00
|
|
|
// Check cache first
|
|
|
|
|
$cacheKey = "user_$username";
|
2026-01-09 16:27:04 -05:00
|
|
|
$cached = self::getCached($cacheKey);
|
|
|
|
|
if ($cached !== null) {
|
|
|
|
|
return $cached;
|
2026-01-01 15:40:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
2026-01-09 16:27:04 -05:00
|
|
|
// Cache user with TTL
|
|
|
|
|
self::setCached($cacheKey, $user);
|
2026-01-01 15:40:32 -05:00
|
|
|
|
|
|
|
|
return $user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get system user (for hwmonDaemon)
|
|
|
|
|
*
|
|
|
|
|
* @return array|null System user data or null if not found
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function getSystemUser(): ?array {
|
2026-01-01 15:40:32 -05:00
|
|
|
// Check cache first
|
2026-01-09 16:27:04 -05:00
|
|
|
$cached = self::getCached('system');
|
|
|
|
|
if ($cached !== null) {
|
|
|
|
|
return $cached;
|
2026-01-01 15:40:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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();
|
2026-01-09 16:27:04 -05:00
|
|
|
self::setCached('system', $user);
|
2026-01-01 15:40:32 -05:00
|
|
|
$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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function getUserById(int $userId): ?array {
|
2026-01-01 15:40:32 -05:00
|
|
|
// Check cache first
|
|
|
|
|
$cacheKey = "user_id_$userId";
|
2026-01-09 16:27:04 -05:00
|
|
|
$cached = self::getCached($cacheKey);
|
|
|
|
|
if ($cached !== null) {
|
|
|
|
|
return $cached;
|
2026-01-01 15:40:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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();
|
2026-01-09 16:27:04 -05:00
|
|
|
self::setCached($cacheKey, $user);
|
2026-01-01 15:40:32 -05:00
|
|
|
$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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function getUserByUsername(string $username): ?array {
|
2026-01-01 15:40:32 -05:00
|
|
|
// Check cache first
|
|
|
|
|
$cacheKey = "user_$username";
|
2026-01-09 16:27:04 -05:00
|
|
|
$cached = self::getCached($cacheKey);
|
|
|
|
|
if ($cached !== null) {
|
|
|
|
|
return $cached;
|
2026-01-01 15:40:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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();
|
2026-01-09 16:27:04 -05:00
|
|
|
self::setCached($cacheKey, $user);
|
2026-01-01 15:40:32 -05:00
|
|
|
$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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
private function checkAdminStatus(string $groups): bool {
|
2026-01-01 15:40:32 -05:00
|
|
|
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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function isAdmin(array $user): bool {
|
2026-01-01 15:40:32 -05:00
|
|
|
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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function hasGroupAccess(array $user, array $requiredGroups = ['admin', 'employee']): bool {
|
2026-01-01 15:40:32 -05:00
|
|
|
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
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function getAllUsers(): array {
|
2026-01-01 15:40:32 -05:00
|
|
|
$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;
|
|
|
|
|
}
|
2026-01-23 10:01:50 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all distinct groups from all users
|
|
|
|
|
* Used for visibility group selection UI
|
|
|
|
|
*
|
2026-01-30 18:31:46 -05:00
|
|
|
* Results are cached for 5 minutes to reduce database load
|
|
|
|
|
* since group changes are infrequent.
|
|
|
|
|
*
|
2026-01-23 10:01:50 -05:00
|
|
|
* @return array Array of unique group names
|
|
|
|
|
*/
|
Add PHP 7.4+ type hints to helpers, models, and middleware
Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:04:36 -05:00
|
|
|
public function getAllGroups(): array {
|
2026-01-30 18:31:46 -05:00
|
|
|
$cacheKey = 'all_groups';
|
|
|
|
|
|
|
|
|
|
// Check cache first
|
|
|
|
|
$cached = self::getCached($cacheKey);
|
|
|
|
|
if ($cached !== null) {
|
|
|
|
|
return $cached;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 10:01:50 -05:00
|
|
|
$stmt = $this->conn->prepare("SELECT DISTINCT groups FROM users WHERE groups IS NOT NULL AND groups != ''");
|
|
|
|
|
$stmt->execute();
|
|
|
|
|
$result = $stmt->get_result();
|
|
|
|
|
|
|
|
|
|
$allGroups = [];
|
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
|
$userGroups = array_filter(array_map('trim', explode(',', $row['groups'])));
|
|
|
|
|
$allGroups = array_merge($allGroups, $userGroups);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stmt->close();
|
|
|
|
|
|
|
|
|
|
// Return unique groups sorted alphabetically
|
|
|
|
|
$uniqueGroups = array_unique($allGroups);
|
|
|
|
|
sort($uniqueGroups);
|
2026-01-30 18:31:46 -05:00
|
|
|
|
|
|
|
|
// Cache the result
|
2026-01-30 19:10:30 -05:00
|
|
|
self::setCached($cacheKey, $uniqueGroups);
|
2026-01-30 18:31:46 -05:00
|
|
|
|
2026-01-23 10:01:50 -05:00
|
|
|
return $uniqueGroups;
|
|
|
|
|
}
|
2026-01-30 18:31:46 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invalidate the groups cache
|
|
|
|
|
* Call this when user groups are modified
|
|
|
|
|
*/
|
|
|
|
|
public static function invalidateGroupsCache(): void {
|
|
|
|
|
unset(self::$userCache['all_groups']);
|
|
|
|
|
}
|
2026-01-01 15:40:32 -05:00
|
|
|
}
|