Files
servicedesk/src/tests/Feature/LdapRestrictedGuestTicketsAndLocalLoginTest.php
2026-07-21 23:39:19 +02:00

174 lines
5.9 KiB
PHP

<?php
use App\Ldap\LldapUser;
use App\Livewire\Admin\Panel;
use App\Livewire\Auth\Login;
use App\Livewire\Landing;
use App\Models\Ticket;
use App\Models\User;
use App\Support\Settings;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use LdapRecord\Laravel\Testing\DirectoryEmulator;
use Livewire\Livewire;
afterEach(function () {
DirectoryEmulator::tearDown();
});
test('restricting guest tickets to LDAP rejects a submission from an unknown e-mail with a not-found message', function () {
DirectoryEmulator::setup();
seedStatusesAndPriorities();
Settings::set('restrict_tickets_to_ldap', '1');
Livewire::test(Landing::class)
->set('email', 'nieznany@firma.pl')
->set('subject', 'Temat')
->set('body', 'Treść')
->call('submit')
->assertHasErrors(['email'])
->assertSee('Nie znaleziono użytkownika o podanym adresie e-mail.');
expect(Ticket::query()->count())->toBe(0);
});
test('restricting guest tickets to LDAP still allows a submission from an e-mail that exists in LDAP', function () {
DirectoryEmulator::setup();
seedStatusesAndPriorities();
Settings::set('restrict_tickets_to_ldap', '1');
LldapUser::create([
'uid' => 'znany.gosc',
'cn' => 'Znany Gość',
'mail' => 'znany.gosc@firma.pl',
'entryuuid' => (string) Str::uuid(),
]);
Livewire::test(Landing::class)
->set('email', 'znany.gosc@firma.pl')
->set('subject', 'Temat')
->set('body', 'Treść')
->call('submit')
->assertHasNoErrors();
expect(Ticket::query()->where('email', 'znany.gosc@firma.pl')->exists())->toBeTrue();
});
test('restricting guest tickets to LDAP still allows an already-known local account even if LDAP lookup fails', function () {
DirectoryEmulator::setup();
seedStatusesAndPriorities();
Settings::set('restrict_tickets_to_ldap', '1');
User::query()->create(['name' => 'Istniejący Klient', 'email' => 'istniejacy@firma.pl', 'roles' => ['client']]);
Livewire::test(Landing::class)
->set('email', 'istniejacy@firma.pl')
->set('subject', 'Temat')
->set('body', 'Treść')
->call('submit')
->assertHasNoErrors();
expect(Ticket::query()->where('email', 'istniejacy@firma.pl')->exists())->toBeTrue();
});
test('leaving the restriction off (the default) allows any guest e-mail to file a ticket', function () {
DirectoryEmulator::setup();
seedStatusesAndPriorities();
Livewire::test(Landing::class)
->set('email', 'ktokolwiek@example.com')
->set('subject', 'Temat')
->set('body', 'Treść')
->call('submit')
->assertHasNoErrors();
expect(Ticket::query()->where('email', 'ktokolwiek@example.com')->exists())->toBeTrue();
});
test('admin can create a local user with a password who can then log in without an LDAP entry', function () {
DirectoryEmulator::setup();
$admin = adminUser();
Livewire::actingAs($admin)->test(Panel::class)
->call('openUserForm')
->set('userForm.name', 'Konto Lokalne')
->set('userForm.email', 'lokalny@firma.pl')
->set('userForm.password', 'sekretne-haslo')
->set('userForm.password_confirmation', 'sekretne-haslo')
->call('submitUser')
->assertOk();
$user = User::query()->where('email', 'lokalny@firma.pl')->firstOrFail();
expect($user->password)->not->toBeNull();
$ok = Auth::attempt([
'uid' => 'lokalny@firma.pl',
'password' => 'sekretne-haslo',
'fallback' => ['email' => 'lokalny@firma.pl'],
]);
expect($ok)->toBeTrue()
->and(Auth::id())->toBe($user->id);
});
test('a mismatched password confirmation is rejected when creating a local user', function () {
$admin = adminUser();
Livewire::actingAs($admin)->test(Panel::class)
->call('openUserForm')
->set('userForm.name', 'Konto Lokalne')
->set('userForm.email', 'zle-haslo@firma.pl')
->set('userForm.password', 'sekretne-haslo')
->set('userForm.password_confirmation', 'inne-haslo')
->call('submitUser')
->assertHasErrors(['userForm.password']);
expect(User::query()->where('email', 'zle-haslo@firma.pl')->exists())->toBeFalse();
});
test('leaving the password blank keeps a user LDAP-only, as before', function () {
$admin = adminUser();
Livewire::actingAs($admin)->test(Panel::class)
->call('openUserForm')
->set('userForm.name', 'Tylko LDAP')
->set('userForm.email', 'tylko-ldap@firma.pl')
->call('submitUser')
->assertOk();
$user = User::query()->where('email', 'tylko-ldap@firma.pl')->firstOrFail();
expect($user->password)->toBeNull();
});
/**
* Exercises Login::safeRedirectTarget() directly via reflection rather than
* through a full ->call('submit') — Livewire's test harness disables the
* middleware stack (see RequestBroker::temporarilyDisableExceptionHandlingAndMiddleware),
* so no session is ever bound to the request during a component test, and
* submit() unconditionally touches the session to regenerate it. The
* open-redirect guard itself has no such dependency, so it's tested in isolation.
*/
function safeRedirectTargetFor(?string $redirect): ?string
{
$login = new Login;
$ref = new ReflectionClass($login);
$prop = $ref->getProperty('redirect');
$prop->setAccessible(true);
$prop->setValue($login, $redirect);
$method = $ref->getMethod('safeRedirectTarget');
$method->setAccessible(true);
return $method->invoke($login);
}
test('a same-app ?redirect= path is honored', function () {
expect(safeRedirectTargetFor('/client/tickets/42'))->toBe('/client/tickets/42');
});
test('an external ?redirect= target is rejected to prevent an open redirect', function () {
expect(safeRedirectTargetFor('https://evil.example.com/'))->toBeNull()
->and(safeRedirectTargetFor('//evil.example.com/'))->toBeNull()
->and(safeRedirectTargetFor(null))->toBeNull();
});