1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-04 17:44:05 +00:00

Clean up impersonation tokens before aborting, add tests

This commit is contained in:
lukinovec 2025-08-06 12:58:18 +02:00
parent 3984d64cfa
commit 2d5b6aa0c8
2 changed files with 68 additions and 2 deletions

View file

@ -44,12 +44,20 @@ class UserImpersonation implements Feature
$tokenExpired = $token->created_at->diffInSeconds(now()) > $ttl;
abort_if($tokenExpired, 403);
if ($tokenExpired) {
$token->delete();
abort(403);
}
$tokenTenantId = (string) $token->getAttribute(Tenancy::tenantKeyColumn());
$currentTenantId = (string) tenant()->getTenantKey();
abort_unless($tokenTenantId === $currentTenantId, 403);
if ($tokenTenantId !== $currentTenantId) {
$token->delete();
abort(403);
}
Auth::guard($token->auth_guard)->loginUsingId($token->user_id, $token->remember);

View file

@ -26,6 +26,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Exceptions\StatefulGuardRequiredException;
use function Stancl\Tenancy\Tests\pest;
use Symfony\Component\HttpKernel\Exception\HttpException;
beforeEach(function () {
pest()->artisan('migrate', [
@ -292,6 +293,63 @@ test('impersonation tokens can be created only with stateful guards', function (
->toBeInstanceOf(ImpersonationToken::class);
});
test('expired tokens are cleaned up before aborting', function () {
$tenant = Tenant::create();
migrateTenants();
$user = $tenant->run(function () {
return ImpersonationUser::create([
'name' => 'foo',
'email' => 'foo@bar',
'password' => bcrypt('password'),
]);
});
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
// Make the token expired
$token->update([
'created_at' => Carbon::now()->subSeconds(100),
]);
expect(ImpersonationToken::find($token->token))->not()->toBeNull();
tenancy()->initialize($tenant);
// Try to use the expired token - should clean up and abort
expect(fn() => UserImpersonation::makeResponse($token->token))
->toThrow(HttpException::class); // Abort with 403
expect(ImpersonationToken::find($token->token))->toBeNull();
});
test('tokens are cleaned up when in wrong tenant context before aborting', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
migrateTenants();
$user = $tenant1->run(function () {
return ImpersonationUser::create([
'name' => 'foo',
'email' => 'foo@bar',
'password' => bcrypt('password'),
]);
});
$token = tenancy()->impersonate($tenant1, $user->id, '/dashboard');
expect(ImpersonationToken::find($token->token))->not->toBeNull();
tenancy()->initialize($tenant2);
// Try to use the token in wrong tenant context - should clean up and abort
expect(fn() => UserImpersonation::makeResponse($token->token))
->toThrow(HttpException::class); // Abort with 403
expect(ImpersonationToken::find($token->token))->toBeNull();
});
function migrateTenants()
{
pest()->artisan('tenants:migrate')->assertExitCode(0);