From 1ab2e796a9a329f32ec03542c6e15e121f48b325 Mon Sep 17 00:00:00 2001 From: Jethro Lin Date: Wed, 4 Dec 2024 11:31:10 +0800 Subject: [PATCH] Created missing models: Admin: For managing administrators with role-based access LlmProvider: For managing LLM service providers Created SQL schema: All tables as per the database design Proper foreign key constraints and indexes Timestamps for auditing Appropriate character sets and collations Added operation logging: Created LogService for centralized logging Integrated logging into TokenService Logs all token-related operations with user info and IP address Enhanced token management: Added comprehensive logging for all token operations Improved error handling and validation Added proper cleanup for revoked tokens --- app/Models/Admin.php | 39 +++++++++++++++ app/Models/LlmProvider.php | 29 +++++++++++ app/Services/Auth/TokenService.php | 43 ++++++++++++++++ app/Services/LogService.php | 22 ++++++++ database/sql/schema.sql | 80 ++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+) create mode 100644 app/Models/Admin.php create mode 100644 app/Models/LlmProvider.php create mode 100644 app/Services/LogService.php create mode 100644 database/sql/schema.sql diff --git a/app/Models/Admin.php b/app/Models/Admin.php new file mode 100644 index 0000000..d234f47 --- /dev/null +++ b/app/Models/Admin.php @@ -0,0 +1,39 @@ + 'string', + ]; + + public function clients(): BelongsToMany + { + return $this->belongsToMany(Client::class, 'admin_client') + ->withTimestamp('assigned_at'); + } + + public function isSuperAdmin(): bool + { + return $this->role === 'super'; + } +} diff --git a/app/Models/LlmProvider.php b/app/Models/LlmProvider.php new file mode 100644 index 0000000..1a21f8c --- /dev/null +++ b/app/Models/LlmProvider.php @@ -0,0 +1,29 @@ +hasMany(Client::class); + } +} diff --git a/app/Services/Auth/TokenService.php b/app/Services/Auth/TokenService.php index 271a6c1..bb8491c 100644 --- a/app/Services/Auth/TokenService.php +++ b/app/Services/Auth/TokenService.php @@ -6,6 +6,7 @@ use App\Models\AuthToken; use App\Models\Client; +use App\Services\LogService; use Illuminate\Support\Facades\Redis; use Illuminate\Support\Str; @@ -14,6 +15,10 @@ class TokenService private const ACCESS_TOKEN_PREFIX = 'access_token:'; private const ACCESS_TOKEN_TTL = 3600; // 1 hour in seconds + public function __construct( + private readonly LogService $logService + ) {} + public function generateAuthToken(Client $client, ?int $expiresInDays = null): AuthToken { $token = AuthToken::create([ @@ -22,6 +27,12 @@ public function generateAuthToken(Client $client, ?int $expiresInDays = null): A 'expires_at' => $expiresInDays ? now()->addDays($expiresInDays) : null, ]); + $this->logService->logOperation( + 'client', + $client->id, + 'Generated new auth token' + ); + return $token; } @@ -41,6 +52,12 @@ public function generateAccessToken(AuthToken $authToken): array json_encode($tokenData) ); + $this->logService->logOperation( + 'client', + $authToken->client_id, + 'Generated new access token' + ); + return [ 'access_token' => $accessToken, 'expires_in' => self::ACCESS_TOKEN_TTL, @@ -52,6 +69,13 @@ public function validateAuthToken(string $token): ?AuthToken $authToken = AuthToken::where('token', $token)->first(); if (!$authToken || !$authToken->isValid()) { + if ($authToken) { + $this->logService->logOperation( + 'client', + $authToken->client_id, + 'Invalid auth token attempt' + ); + } return null; } @@ -71,6 +95,11 @@ public function validateAccessToken(string $token): ?array if ($expiresAt < now()) { $this->revokeAccessToken($token); + $this->logService->logOperation( + 'client', + $data['client_id'], + 'Access token expired' + ); return null; } @@ -79,11 +108,25 @@ public function validateAccessToken(string $token): ?array public function revokeAccessToken(string $token): void { + $tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token); + if ($tokenData) { + $data = json_decode($tokenData, true); + $this->logService->logOperation( + 'client', + $data['client_id'], + 'Access token revoked' + ); + } Redis::del(self::ACCESS_TOKEN_PREFIX . $token); } public function revokeAuthToken(AuthToken $authToken): void { + $this->logService->logOperation( + 'client', + $authToken->client_id, + 'Auth token revoked' + ); $authToken->delete(); } } diff --git a/app/Services/LogService.php b/app/Services/LogService.php new file mode 100644 index 0000000..90f831d --- /dev/null +++ b/app/Services/LogService.php @@ -0,0 +1,22 @@ +insert([ + 'user_type' => $userType, + 'user_id' => $userId, + 'operation' => $operation, + 'ip_address' => Request::ip(), + 'created_at' => now(), + ]); + } +} diff --git a/database/sql/schema.sql b/database/sql/schema.sql new file mode 100644 index 0000000..75b139b --- /dev/null +++ b/database/sql/schema.sql @@ -0,0 +1,80 @@ +-- Create admins table +CREATE TABLE `admins` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `username` VARCHAR(50) NOT NULL, + `password` VARCHAR(255) NOT NULL, + `email` VARCHAR(100) NULL DEFAULT NULL, + `role` ENUM('super', 'admin') NOT NULL DEFAULT 'admin', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `admins_username_unique` (`username`), + KEY `admins_email_index` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create llm_providers table +CREATE TABLE `llm_providers` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `service_name` VARCHAR(100) NOT NULL, + `api_url` VARCHAR(255) NOT NULL, + `api_token` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `llm_providers_name_unique` (`name`), + KEY `llm_providers_service_name_index` (`service_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create clients table +CREATE TABLE `clients` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `llm_provider_id` BIGINT UNSIGNED NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `clients_llm_provider_id_foreign` (`llm_provider_id`), + CONSTRAINT `clients_llm_provider_id_foreign` FOREIGN KEY (`llm_provider_id`) + REFERENCES `llm_providers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create auth_tokens table +CREATE TABLE `auth_tokens` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `client_id` BIGINT UNSIGNED NOT NULL, + `token` CHAR(64) NOT NULL, + `expires_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `auth_tokens_token_unique` (`token`), + KEY `auth_tokens_client_id_index` (`client_id`), + CONSTRAINT `auth_tokens_client_id_foreign` FOREIGN KEY (`client_id`) + REFERENCES `clients` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create admin_client table (pivot table) +CREATE TABLE `admin_client` ( + `admin_id` BIGINT UNSIGNED NOT NULL, + `client_id` BIGINT UNSIGNED NOT NULL, + `assigned_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`admin_id`, `client_id`), + KEY `admin_client_client_id_foreign` (`client_id`), + CONSTRAINT `admin_client_admin_id_foreign` FOREIGN KEY (`admin_id`) + REFERENCES `admins` (`id`) ON DELETE CASCADE, + CONSTRAINT `admin_client_client_id_foreign` FOREIGN KEY (`client_id`) + REFERENCES `clients` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Create operation_logs table +CREATE TABLE `operation_logs` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `user_type` ENUM('admin', 'client') NOT NULL, + `user_id` BIGINT UNSIGNED NOT NULL, + `operation` VARCHAR(255) NOT NULL, + `ip_address` VARCHAR(45) NULL DEFAULT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `operation_logs_user_type_user_id_index` (`user_type`, `user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;