Implements Spec 083 (Canonical Required Permissions manage surface hardening + issues-first UX).
Highlights:
- Enforces canonical route: /admin/tenants/{tenant}/required-permissions
- Legacy tenant-plane URL /admin/t/{tenant}/required-permissions stays non-existent (404)
- Deny-as-not-found (404) for non-workspace members and non-tenant-entitled users
- Strict tenant resolution (no cross-plane fallback)
- DB-only render (no external provider calls on page load)
- Issues-first layout + canonical next-step links (re-run verification -> /admin/onboarding)
- Freshness/stale detection (missing or >30 days -> warning)
Tests (Sail):
- vendor/bin/sail artisan test --compact tests/Feature/RequiredPermissions
- vendor/bin/sail artisan test --compact tests/Unit/TenantRequiredPermissionsFreshnessTest.php tests/Unit/TenantRequiredPermissionsOverallStatusTest.php
Notes:
- Filament v5 / Livewire v4 compliant.
- No destructive actions added in this spec; link-only CTAs.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #101
64 lines
2.1 KiB
PHP
64 lines
2.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Models\WorkspaceMembership;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
it('returns 200 for tenant-entitled readonly members on the canonical required permissions route', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'readonly');
|
|
|
|
$this->actingAs($user)
|
|
->get("/admin/tenants/{$tenant->external_id}/required-permissions")
|
|
->assertOk();
|
|
});
|
|
|
|
it('returns 404 for workspace members without tenant entitlement on the canonical route', function (): void {
|
|
$user = User::factory()->create();
|
|
$workspace = Workspace::factory()->create();
|
|
$tenant = Tenant::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
]);
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
|
|
])
|
|
->get("/admin/tenants/{$tenant->external_id}/required-permissions")
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('returns 404 for users who are not workspace members', function (): void {
|
|
$user = User::factory()->create();
|
|
$workspace = Workspace::factory()->create();
|
|
$tenant = Tenant::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
|
|
])
|
|
->get("/admin/tenants/{$tenant->external_id}/required-permissions")
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('returns 404 when the route tenant is invalid instead of falling back to the current tenant context', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'readonly');
|
|
|
|
Tenant::query()->whereKey((int) $tenant->getKey())->update(['is_current' => true]);
|
|
|
|
$this->actingAs($user)
|
|
->get('/admin/tenants/invalid-tenant-id/required-permissions')
|
|
->assertNotFound();
|
|
});
|