diff --git a/config/user-monitoring.php b/config/user-monitoring.php index 0090b06..a5be916 100644 --- a/config/user-monitoring.php +++ b/config/user-monitoring.php @@ -32,9 +32,14 @@ 'table' => 'users', /* - * The correct guard. + * You can customize which guards are used to authenticate or + * store user data across different parts of the application. Each guard + * will be checked independently, allowing users to be authenticated by + * multiple guards and enabling more flexible user management. + * + * Make sure that each guard is properly configured under the 'guards' section in the auth.php config file. */ - 'guard' => 'web', + 'guards' => ['web'], /* * If you are using uuid or ulid you can change it for the type of foreign_key. @@ -100,8 +105,8 @@ 'on_read' => true, 'on_restore' => false, 'on_replicate' => false, - - /** + + /** * Determines if the application should use reverse proxy headers to fetch the real client IP * If set to true, it will try to get the IP from the specified header (X-Real-IP or X-Forwarded-For) * This is useful when using reverse proxies like Nginx or Cloudflare. diff --git a/database/migrations/2023_07_22_230401_create_visits_monitoring_table.php b/database/migrations/2023_07_22_230401_create_visits_monitoring_table.php index 65b3503..92a3f92 100644 --- a/database/migrations/2023_07_22_230401_create_visits_monitoring_table.php +++ b/database/migrations/2023_07_22_230401_create_visits_monitoring_table.php @@ -21,7 +21,9 @@ public function up(): void $table->string('platform'); $table->string('device'); $table->string('ip'); + $table->string('user_guard')->nullable(); $table->text('page'); + $table->timestamps(); }); } diff --git a/database/migrations/2023_07_23_145723_create_actions_monitoring_table.php b/database/migrations/2023_07_23_145723_create_actions_monitoring_table.php index a2f65c4..4651cb3 100644 --- a/database/migrations/2023_07_23_145723_create_actions_monitoring_table.php +++ b/database/migrations/2023_07_23_145723_create_actions_monitoring_table.php @@ -24,7 +24,9 @@ public function up(): void $table->string('platform'); $table->string('device'); $table->string('ip'); + $table->string('user_guard')->nullable(); $table->text('page'); + $table->timestamps(); }); } diff --git a/database/migrations/2023_07_25_132642_create_authentications_monitoring_table.php b/database/migrations/2023_07_25_132642_create_authentications_monitoring_table.php index 06dbd26..c6f8396 100644 --- a/database/migrations/2023_07_25_132642_create_authentications_monitoring_table.php +++ b/database/migrations/2023_07_25_132642_create_authentications_monitoring_table.php @@ -41,7 +41,9 @@ public function up(): void $table->string('platform'); $table->string('device'); $table->string('ip'); + $table->string('user_guard')->nullable(); $table->text('page'); + $table->timestamps(); }); } diff --git a/src/Middlewares/VisitMonitoringMiddleware.php b/src/Middlewares/VisitMonitoringMiddleware.php index faf98b2..1c2a34f 100644 --- a/src/Middlewares/VisitMonitoringMiddleware.php +++ b/src/Middlewares/VisitMonitoringMiddleware.php @@ -3,6 +3,7 @@ namespace Binafy\LaravelUserMonitoring\Middlewares; use Binafy\LaravelUserMonitoring\Utills\Detector; +use Binafy\LaravelUserMonitoring\Utills\UserUtils; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -22,17 +23,17 @@ public function handle(Request $request, Closure $next): mixed } $detector = new Detector(); - $guard = config('user-monitoring.user.guard', 'web'); $exceptPages = config('user-monitoring.visit_monitoring.except_pages', []); if (empty($exceptPages) || !$this->checkIsExceptPages($request->path(), $exceptPages)) { // Store visit DB::table(config('user-monitoring.visit_monitoring.table'))->insert([ - 'user_id' => auth($guard)->id(), + 'user_id' => UserUtils::getUserId(), 'browser_name' => $detector->getBrowser(), 'platform' => $detector->getDevice(), 'device' => $detector->getDevice(), 'ip' => $request->ip(), + 'user_guard' => UserUtils::getCurrentGuardName(), 'page' => $request->url(), 'created_at' => now(), 'updated_at' => now(), @@ -45,7 +46,7 @@ public function handle(Request $request, Closure $next): mixed /** * Check request page are exists in expect pages. */ - private function checkIsExceptPages(string $page, array $exceptPages): bool + protected function checkIsExceptPages(string $page, array $exceptPages): bool { return collect($exceptPages)->contains($page); } diff --git a/src/Models/ActionMonitoring.php b/src/Models/ActionMonitoring.php index 617cfd1..74827b1 100644 --- a/src/Models/ActionMonitoring.php +++ b/src/Models/ActionMonitoring.php @@ -2,6 +2,7 @@ namespace Binafy\LaravelUserMonitoring\Models; +use Binafy\LaravelUserMonitoring\Utills\ActionType; use Illuminate\Database\Eloquent\Model; class ActionMonitoring extends Model @@ -22,21 +23,27 @@ class ActionMonitoring extends Model # Methods + /** + * Get the type color by action type. + */ public function getTypeColor(): string { return match ($this->action_type) { - 'read' => 'blue', - 'store' => 'green', - 'update' => 'purple', - 'delete' => 'red', - 'restore' => 'yellow', - 'replicate' => 'pink', + ActionType::ACTION_READ => 'blue', + ActionType::ACTION_STORE => 'green', + ActionType::ACTION_UPDATE => 'purple', + ActionType::ACTION_DELETE => 'red', + ActionType::ACTION_RESTORED => 'yellow', + ActionType::ACTION_REPLICATE => 'pink', default => 'gray', }; } # Relations + /** + * Relation one-to-many, User model. + */ public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo( diff --git a/src/Providers/LaravelUserMonitoringEventServiceProvider.php b/src/Providers/LaravelUserMonitoringEventServiceProvider.php index aa1c350..50adb36 100644 --- a/src/Providers/LaravelUserMonitoringEventServiceProvider.php +++ b/src/Providers/LaravelUserMonitoringEventServiceProvider.php @@ -3,6 +3,7 @@ namespace Binafy\LaravelUserMonitoring\Providers; use Binafy\LaravelUserMonitoring\Utills\Detector; +use Binafy\LaravelUserMonitoring\Utills\UserUtils; use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Logout; use Illuminate\Foundation\Support\Providers\EventServiceProvider; @@ -14,25 +15,24 @@ class LaravelUserMonitoringEventServiceProvider extends EventServiceProvider public function boot(): void { $detector = new Detector(); - $guard = config('user-monitoring.user.guard'); $table = config('user-monitoring.authentication_monitoring.table'); // Login Event if (config('user-monitoring.authentication_monitoring.on_login', false)) { - Event::listen(function (Login $event) use ($detector, $guard, $table) { + Event::listen(function (Login $event) use ($detector, $table) { DB::table($table) ->insert( - $this->insertData($guard, $detector, 'login'), + $this->insertData($detector, 'login'), ); }); } // Logout Event if (config('user-monitoring.authentication_monitoring.on_logout', false)) { - Event::listen(function (Logout $event) use ($detector, $guard, $table) { + Event::listen(function (Logout $event) use ($detector, $table) { DB::table($table) ->insert( - $this->insertData($guard, $detector, 'logout'), + $this->insertData($detector, 'logout'), ); }); } @@ -41,15 +41,16 @@ public function boot(): void /** * Get insert data. */ - private function insertData(string $guard, Detector $detector, string $actionType): array + private function insertData(Detector $detector, string $actionType): array { return [ - 'user_id' => auth($guard)->id(), + 'user_id' => UserUtils::getUserId(), 'action_type' => $actionType, 'browser_name' => $detector->getBrowser(), 'platform' => $detector->getDevice(), 'device' => $detector->getDevice(), 'ip' => request()->ip(), + 'user_guard' => UserUtils::getCurrentGuardName(), 'page' => request()->url(), 'created_at' => now(), 'updated_at' => now(), diff --git a/src/Traits/Actionable.php b/src/Traits/Actionable.php index b89c2d6..fac1c9a 100644 --- a/src/Traits/Actionable.php +++ b/src/Traits/Actionable.php @@ -4,6 +4,7 @@ use Binafy\LaravelUserMonitoring\Utills\ActionType; use Binafy\LaravelUserMonitoring\Utills\Detector; +use Binafy\LaravelUserMonitoring\Utills\UserUtils; use Illuminate\Support\Facades\DB; trait Actionable @@ -64,16 +65,16 @@ protected static function boot(): void private static function insertActionMonitoring(mixed $model, string $actionType): void { $detector = new Detector; - $guard = config('user-monitoring.user.guard'); DB::table(config('user-monitoring.action_monitoring.table'))->insert([ - 'user_id' => auth($guard)->id(), + 'user_id' => UserUtils::getUserId(), 'action_type' => $actionType, 'table_name' => $model->getTable(), 'browser_name' => $detector->getBrowser(), 'platform' => $detector->getDevice(), 'device' => $detector->getDevice(), 'ip' => self::getRealIP(), + 'user_guard' => UserUtils::getCurrentGuardName(), 'page' => request()->url(), 'created_at' => now(), 'updated_at' => now(), diff --git a/src/Utills/ActionType.php b/src/Utills/ActionType.php index 9811f39..67bf84a 100644 --- a/src/Utills/ActionType.php +++ b/src/Utills/ActionType.php @@ -10,4 +10,13 @@ class ActionType public const ACTION_READ = 'read'; public const ACTION_RESTORED = 'restore'; public const ACTION_REPLICATE = 'replicate'; + + public static array $types = [ + self::ACTION_STORE, + self::ACTION_UPDATE, + self::ACTION_DELETE, + self::ACTION_READ, + self::ACTION_RESTORED, + self::ACTION_REPLICATE, + ]; } diff --git a/src/Utills/UserUtils.php b/src/Utills/UserUtils.php index 9fc4104..058e4a9 100644 --- a/src/Utills/UserUtils.php +++ b/src/Utills/UserUtils.php @@ -3,16 +3,14 @@ namespace Binafy\LaravelUserMonitoring\Utills; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Auth; class UserUtils { /** * Return user foreign key by ulid, uuid, id. - * - * @param Blueprint $table - * @return void */ - public static function userForeignKey(Blueprint $table) + public static function userForeignKey(Blueprint $table): void { $type = config('user-monitoring.user.foreign_key_type', 'id'); @@ -33,4 +31,38 @@ public static function userForeignKey(Blueprint $table) ->nullOnDelete(); } } + + /** + * Get the current guard name. + */ + public static function getCurrentGuardName(): ?string + { + $guards = array_keys(config('auth.guards')); // Get all guard names from config + + foreach ($guards as $guard) { + if (Auth::guard($guard)->check()) { + return $guard; + } + } + + return null; + } + + /** + * Get the user id by guards. + */ + public static function getUserId(): ?int + { + $guards = config('user-monitoring.user.guards', ['web']); + + foreach ($guards as $guard) { + try { + if (Auth::guard($guard)->check()) { + return Auth::guard($guard)->id(); + } + } catch (\Exception $e) {} + } + + return null; + } } diff --git a/tests/Feature/ActionMonitoringTest.php b/tests/Feature/ActionMonitoringTest.php index 60abe66..3096c12 100644 --- a/tests/Feature/ActionMonitoringTest.php +++ b/tests/Feature/ActionMonitoringTest.php @@ -2,7 +2,9 @@ use Binafy\LaravelUserMonitoring\Models\ActionMonitoring; use Binafy\LaravelUserMonitoring\Utills\ActionType; +use Binafy\LaravelUserMonitoring\Utills\UserUtils; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\DB; use Tests\SetUp\Models\Product; use function Pest\Laravel\{assertDatabaseCount, assertDatabaseHas}; @@ -32,6 +34,29 @@ assertDatabaseHas(config('user-monitoring.action_monitoring.table'), ['page' => url('/')]); }); +test('store action monitoring when a model created with login user with unknow guard', function () { + config(['user-monitoring.user.guards' => ['milwad']]); + + $user = createUser(); + auth()->login($user); + + Product::query()->create([ + 'title' => 'milwad' + ]); + + // Assertions + expect(ActionMonitoring::query()->value('table_name')) + ->toBe('products') + ->and(ActionMonitoring::query()->value('action_type')) + ->toBe(ActionType::ACTION_STORE) + ->and(ActionMonitoring::first()->user) + ->toBeNull(); + + // DB Assertions + assertDatabaseCount(config('user-monitoring.action_monitoring.table'), 1); + assertDatabaseHas(config('user-monitoring.action_monitoring.table'), ['page' => url('/')]); +}); + test('store action monitoring when a model created without login user', function () { Product::query()->create([ 'title' => 'milwad' @@ -248,3 +273,32 @@ assertDatabaseCount(config('user-monitoring.action_monitoring.table'), 3); assertDatabaseHas(config('user-monitoring.action_monitoring.table'), ['page' => url('/')]); }); + +test('the getTypeColor method work as expected', function () { + $defaultData = [ + 'user_id' => null, + 'table_name' => 'products', + 'browser_name' => 'Chrome', + 'platform' => 'Windows', + 'device' => 'Macbook M4', + 'ip' => '192.168.0.1', + 'user_guard' => 'web', + 'page' => 'https://github.com/milwad-dev', + ]; + $colors = [ + ActionType::ACTION_READ => 'blue', + ActionType::ACTION_STORE => 'green', + ActionType::ACTION_UPDATE => 'purple', + ActionType::ACTION_DELETE => 'red', + ActionType::ACTION_RESTORED => 'yellow', + ActionType::ACTION_REPLICATE => 'pink', + ]; + + foreach (ActionType::$types as $type) { + $actionMonitoring = ActionMonitoring::query()->create($defaultData + [ + 'action_type' => $type + ]); + + expect($actionMonitoring->getTypeColor())->toBe($colors[$type]); + } +});