CluPilotCloud/tests/Feature/Auth/ConsoleLoginLandsInTheConso...

124 lines
4.8 KiB
PHP

<?php
use App\Models\Customer;
use App\Models\User;
/**
* Signing in lands where it was performed.
*
* Fortify sends everyone to /dashboard. That is why an operator who signed in
* ended up in the customer portal — and once the console has a hostname to
* itself, /dashboard does not exist there at all, so it would have been a 404.
*/
beforeEach(function () {
config()->set('admin_access.hosts', ['admin.example.test']);
config()->set('admin_access.exclusive', true);
});
it('refuses an operator at the shared portal login — the account is not in that table any more', function () {
// Before the identity move this landed in the console (that is what this
// file used to prove). Now there is no operator row Fortify's web-guard
// attempt could ever match, on any host — refusing it here is the correct
// behaviour, not a gap Task 5's dedicated console login has yet to close.
$operator = operator('Owner');
$this->post('http://admin.example.test/login', [
'email' => $operator->email,
'password' => 'password',
])->assertSessionHasErrors('email');
expect(auth()->check())->toBeFalse();
});
it('sends a customer signing in on the portal host to the portal', function () {
$user = User::factory()->create();
$this->post('http://app.example.test/login', [
'email' => $user->email,
'password' => 'password',
])->assertRedirect('/dashboard');
});
it('refuses to leave a customer holding a session on the console host', function () {
// Fortify authenticates before anything downstream can object, so the
// session already exists. Refusing each page afterwards would leave it in
// the browser; it is taken away at the moment it was created.
$customer = Customer::factory()->create();
$user = $customer->ensureUser();
$user->forceFill(['password' => bcrypt('password')])->save();
$this->post('http://admin.example.test/login', [
'email' => $user->email,
'password' => 'password',
])->assertSessionHasErrors('email');
expect(auth()->check())->toBeFalse();
});
it('makes the same decision after a two-factor challenge', function () {
// Fortify never reaches LoginResponse on this path. Binding only that one
// left the accounts most likely to be operators landing in the portal.
$operator = operator('Owner');
$response = app(Laravel\Fortify\Contracts\TwoFactorLoginResponse::class)
->toResponse(tap(request(), function ($request) use ($operator) {
$request->headers->set('HOST', 'admin.example.test');
$request->setUserResolver(fn () => $operator);
}));
// The console root on the console host — emphatically not the portal.
expect($response->getTargetUrl())->toContain('admin.example.test')
->and($response->getTargetUrl())->not->toContain('/dashboard');
});
it('gives a JSON client the same refusal as a browser gets', function () {
// bootstrap/app.php restricts JSON exception rendering to api/* paths, so
// a failed /login attempt redirects with session errors regardless of the
// caller's Accept header — there is no separate JSON error shape here for
// an operator's address to diverge from. What still matters is that a
// JSON-flavoured request does not somehow slip past that and end up
// authenticated.
$operator = operator('Owner');
$this->postJson('http://admin.example.test/login', [
'email' => $operator->email,
'password' => 'password',
])->assertRedirect();
expect(auth()->check())->toBeFalse();
});
it('does not let a JSON client keep a session a browser would lose', function () {
// Answering JSON early skipped the permission check entirely.
$customer = Customer::factory()->create();
$user = $customer->ensureUser();
$user->forceFill(['password' => bcrypt('password')])->save();
$this->postJson('http://admin.example.test/login', [
'email' => $user->email,
'password' => 'password',
])->assertForbidden();
expect(auth()->check())->toBeFalse();
});
it('removes the session of a non-operator who was heading for /admin on a shared host', function () {
// In shared mode everyone posts to /login, so the request never looks like
// the console. Without reading the intended destination, a non-operator was
// authenticated, shown a 403 — and left holding the session.
config()->set('admin_access.exclusive', false);
config()->set('admin_access.hosts', []);
$customer = Customer::factory()->create();
$user = $customer->ensureUser();
$user->forceFill(['password' => bcrypt('password')])->save();
$this->get('/admin')->assertRedirect('/login');
$this->post('/login', ['email' => $user->email, 'password' => 'password'])
->assertSessionHasErrors('email');
expect(auth()->check())->toBeFalse();
});