From fc0a260819dda9f20465b6a641995e7ae429f55d Mon Sep 17 00:00:00 2001 From: Yurii PLohov Date: Wed, 23 Mar 2022 16:43:03 +0600 Subject: [PATCH 1/3] [2.x] Add sanctum:prune-expired command for removing expired tokens. --- composer.json | 1 + src/Console/Commands/PruneExpired.php | 44 +++++++++++ src/SanctumServiceProvider.php | 5 ++ tests/PruneExpiredTest.php | 108 ++++++++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 src/Console/Commands/PruneExpired.php create mode 100644 tests/PruneExpiredTest.php diff --git a/composer.json b/composer.json index 23d8436f..1c274853 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "require": { "php": "^7.2|^8.0", "ext-json": "*", + "illuminate/console": "^6.9|^7.0|^8.0|^9.0", "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0", "illuminate/database": "^6.9|^7.0|^8.0|^9.0", "illuminate/support": "^6.9|^7.0|^8.0|^9.0" diff --git a/src/Console/Commands/PruneExpired.php b/src/Console/Commands/PruneExpired.php new file mode 100644 index 00000000..f59b8a23 --- /dev/null +++ b/src/Console/Commands/PruneExpired.php @@ -0,0 +1,44 @@ +option('hours'); + + $model::where('created_at', '<', now()->subMinutes($expiration + ($hours * 60)))->delete(); + + $this->info("Tokens expired for more than {$hours} hours pruned successfully."); + } + + $this->warn("Expiration value not specified in configuration file."); + } +} diff --git a/src/SanctumServiceProvider.php b/src/SanctumServiceProvider.php index 28e1bbf0..27c00721 100644 --- a/src/SanctumServiceProvider.php +++ b/src/SanctumServiceProvider.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Laravel\Sanctum\Console\Commands\PruneExpired; use Laravel\Sanctum\Http\Controllers\CsrfCookieController; use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful; @@ -48,6 +49,10 @@ public function boot() $this->publishes([ __DIR__.'/../config/sanctum.php' => config_path('sanctum.php'), ], 'sanctum-config'); + + $this->commands([ + PruneExpired::class, + ]); } $this->defineRoutes(); diff --git a/tests/PruneExpiredTest.php b/tests/PruneExpiredTest.php new file mode 100644 index 00000000..be3664de --- /dev/null +++ b/tests/PruneExpiredTest.php @@ -0,0 +1,108 @@ +set('database.default', 'testbench'); + + $app['config']->set('database.connections.testbench', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } + + public function test_can_delete_expired_tokens_with_integer_expiration() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + config(['sanctum.expiration' => 60]); + + $user = UserForPruneExpiredTest::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + ]); + + $token_1 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_1', + 'token' => hash('sha256', 'test_1'), + 'created_at' => now()->subMinutes(181), + ]); + + $token_2 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_2', + 'token' => hash('sha256', 'test_2'), + 'created_at' => now()->subMinutes(179), + ]); + + $token_3 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_3', + 'token' => hash('sha256', 'test_3'), + 'created_at' => now()->subMinutes(121), + ]); + + $this->artisan('sanctum:prune-expired --hours=2') + ->expectsOutput('Tokens expired for more than 2 hours pruned successfully.'); + + $this->assertDatabaseMissing('personal_access_tokens', ['name' => 'Test_1']); + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test_2']); + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test_3']); + } + + public function test_cant_delete_expired_tokens_with_null_expiration() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + config(['sanctum.expiration' => null]); + + $user = UserForPruneExpiredTest::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + ]); + + $token = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test', + 'token' => hash('sha256', 'test'), + 'created_at' => now()->subMinutes(70), + ]); + + $this->artisan('sanctum:prune-expired --hours=2') + ->expectsOutput('Expiration value not specified in configuration file.'); + + $this->assertDatabaseCount('personal_access_tokens', 1); + } + + protected function getPackageProviders($app) + { + return [SanctumServiceProvider::class]; + } +} + +class UserForPruneExpiredTest extends User implements HasApiTokensContract +{ + use HasApiTokens; + + protected $table = 'users'; +} From e1442f6f350bdd731f977ce4ebdddd0885564f04 Mon Sep 17 00:00:00 2001 From: Yurii PLohov Date: Wed, 23 Mar 2022 18:21:12 +0600 Subject: [PATCH 2/3] Removed the assertDatabaseCount() method from PruneExpiredTest code --- tests/PruneExpiredTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PruneExpiredTest.php b/tests/PruneExpiredTest.php index be3664de..b28b08af 100644 --- a/tests/PruneExpiredTest.php +++ b/tests/PruneExpiredTest.php @@ -91,7 +91,7 @@ public function test_cant_delete_expired_tokens_with_null_expiration() $this->artisan('sanctum:prune-expired --hours=2') ->expectsOutput('Expiration value not specified in configuration file.'); - $this->assertDatabaseCount('personal_access_tokens', 1); + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test']); } protected function getPackageProviders($app) From 811d220a85612742c6181da7eb18e9c2f8ddb313 Mon Sep 17 00:00:00 2001 From: Yurii PLohov Date: Wed, 23 Mar 2022 18:28:33 +0600 Subject: [PATCH 3/3] Fixed style in the PruneExpired command --- src/Console/Commands/PruneExpired.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Console/Commands/PruneExpired.php b/src/Console/Commands/PruneExpired.php index f59b8a23..fad1c83c 100644 --- a/src/Console/Commands/PruneExpired.php +++ b/src/Console/Commands/PruneExpired.php @@ -29,7 +29,6 @@ class PruneExpired extends Command public function handle() { if ($expiration = config('sanctum.expiration')) { - $model = Sanctum::$personalAccessTokenModel; $hours = $this->option('hours'); @@ -39,6 +38,6 @@ public function handle() $this->info("Tokens expired for more than {$hours} hours pruned successfully."); } - $this->warn("Expiration value not specified in configuration file."); + $this->warn('Expiration value not specified in configuration file.'); } }