# Conflicts: # app/Filament/Resources/TenantResource.php # app/Filament/Resources/TenantResource/Pages/CreateTenant.php # app/Filament/Resources/TenantResource/Pages/ListTenants.php # app/Providers/Filament/AdminPanelProvider.php # tests/Feature/Filament/TenantSetupTest.php
202 lines
5.9 KiB
PHP
202 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages\ManagedTenants;
|
|
|
|
use App\Filament\Resources\TenantResource;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Services\Auth\WorkspaceCapabilityResolver;
|
|
use App\Services\Intune\AuditLogger;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Concerns\InteractsWithForms;
|
|
use Filament\Forms\Contracts\HasForms;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Schemas\Schema;
|
|
|
|
class Onboarding extends Page implements HasForms
|
|
{
|
|
use InteractsWithForms;
|
|
|
|
protected static bool $shouldRegisterNavigation = false;
|
|
|
|
protected static bool $isDiscovered = false;
|
|
|
|
protected static ?string $slug = 'managed-tenants/onboarding';
|
|
|
|
protected static ?string $title = 'Add managed tenant';
|
|
|
|
protected string $view = 'filament.pages.managed-tenants.onboarding';
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
public array $data = [];
|
|
|
|
public function mount(): void
|
|
{
|
|
static::abortIfNonMember();
|
|
|
|
if (! static::canView()) {
|
|
abort(403);
|
|
}
|
|
|
|
$this->form->fill();
|
|
}
|
|
|
|
public static function canView(): bool
|
|
{
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
$workspace = static::resolveCurrentWorkspaceFor($user);
|
|
|
|
if (! $workspace instanceof Workspace) {
|
|
return false;
|
|
}
|
|
|
|
/** @var WorkspaceCapabilityResolver $resolver */
|
|
$resolver = app(WorkspaceCapabilityResolver::class);
|
|
|
|
return $resolver->can($user, $workspace, Capabilities::WORKSPACE_MEMBERSHIP_MANAGE);
|
|
}
|
|
|
|
public function form(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->schema([
|
|
Forms\Components\TextInput::make('name')
|
|
->required()
|
|
->maxLength(255),
|
|
Forms\Components\Select::make('environment')
|
|
->options([
|
|
'prod' => 'PROD',
|
|
'dev' => 'DEV',
|
|
'staging' => 'STAGING',
|
|
'other' => 'Other',
|
|
])
|
|
->default('other')
|
|
->required(),
|
|
Forms\Components\TextInput::make('tenant_id')
|
|
->label('Tenant ID (GUID)')
|
|
->required()
|
|
->maxLength(255)
|
|
->unique(ignoreRecord: true),
|
|
Forms\Components\TextInput::make('domain')
|
|
->label('Primary domain')
|
|
->maxLength(255),
|
|
Forms\Components\TextInput::make('app_client_id')
|
|
->label('App Client ID')
|
|
->maxLength(255),
|
|
Forms\Components\TextInput::make('app_client_secret')
|
|
->label('App Client Secret')
|
|
->password()
|
|
->dehydrateStateUsing(fn ($state) => filled($state) ? $state : null)
|
|
->dehydrated(fn ($state) => filled($state)),
|
|
Forms\Components\TextInput::make('app_certificate_thumbprint')
|
|
->label('Certificate thumbprint')
|
|
->maxLength(255),
|
|
Forms\Components\Textarea::make('app_notes')
|
|
->label('Notes')
|
|
->rows(3),
|
|
])
|
|
->statePath('data');
|
|
}
|
|
|
|
public function create(AuditLogger $auditLogger): void
|
|
{
|
|
static::abortIfNonMember();
|
|
|
|
if (! static::canView()) {
|
|
abort(403);
|
|
}
|
|
|
|
$data = $this->form->getState();
|
|
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
$workspace = static::resolveCurrentWorkspaceFor($user);
|
|
|
|
if (! $workspace instanceof Workspace) {
|
|
abort(403);
|
|
}
|
|
|
|
$data['workspace_id'] = (int) $workspace->getKey();
|
|
|
|
$tenant = Tenant::query()->create($data);
|
|
|
|
if ($user instanceof User) {
|
|
$user->tenants()->syncWithoutDetaching([
|
|
$tenant->getKey() => [
|
|
'role' => 'owner',
|
|
'source' => 'manual',
|
|
'created_by_user_id' => $user->getKey(),
|
|
],
|
|
]);
|
|
|
|
$auditLogger->log(
|
|
tenant: $tenant,
|
|
action: 'managed_tenant.onboarding.created',
|
|
context: [
|
|
'metadata' => [
|
|
'internal_tenant_id' => (int) $tenant->getKey(),
|
|
'tenant_guid' => (string) $tenant->tenant_id,
|
|
],
|
|
],
|
|
actorId: (int) $user->getKey(),
|
|
actorEmail: $user->email,
|
|
actorName: $user->name,
|
|
status: 'success',
|
|
resourceType: 'tenant',
|
|
resourceId: (string) $tenant->getKey(),
|
|
);
|
|
}
|
|
|
|
Notification::make()
|
|
->title('Managed tenant added')
|
|
->success()
|
|
->send();
|
|
|
|
$this->redirect(TenantResource::getUrl('view', ['record' => $tenant]));
|
|
}
|
|
|
|
private static function abortIfNonMember(): void
|
|
{
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
if (! static::resolveCurrentWorkspaceFor($user) instanceof Workspace) {
|
|
abort(404);
|
|
}
|
|
}
|
|
|
|
private static function resolveCurrentWorkspaceFor(User $user): ?Workspace
|
|
{
|
|
/** @var WorkspaceContext $context */
|
|
$context = app(WorkspaceContext::class);
|
|
|
|
$workspace = $context->resolveInitialWorkspaceFor($user, request());
|
|
|
|
if (! $workspace instanceof Workspace) {
|
|
return null;
|
|
}
|
|
|
|
return $context->isMember($user, $workspace) ? $workspace : null;
|
|
}
|
|
}
|