matrix = $matrix; } /** * Authorize access - Hades tier only. */ public function mount(): void { if (! auth()->user()?->isHades()) { abort(403, 'Hades tier required for permission matrix management.'); } } public function updatingSearch(): void { $this->resetPage(); } public function updatingEntityFilter(): void { $this->resetPage(); } public function openTrain(?int $requestId = null): void { if ($requestId) { $request = PermissionRequest::find($requestId); if ($request) { $this->trainingEntityId = $request->entity_id; $this->trainingKey = $request->action; $this->trainingScope = $request->scope ?? ''; } } $this->trainingAllow = true; $this->trainingLock = false; $this->showTrainModal = true; } public function openTrainNew(): void { $this->trainingEntityId = null; $this->trainingKey = ''; $this->trainingScope = ''; $this->trainingAllow = true; $this->trainingLock = false; $this->showTrainModal = true; } public function train(): void { $this->validate([ 'trainingEntityId' => 'required|exists:commerce_entities,id', 'trainingKey' => 'required|string|max:255', 'trainingScope' => 'nullable|string|max:255', ]); $entity = Entity::findOrFail($this->trainingEntityId); try { if ($this->trainingLock) { $this->matrix->lock( entity: $entity, key: $this->trainingKey, allowed: $this->trainingAllow, scope: $this->trainingScope ?: null ); } else { $this->matrix->train( entity: $entity, key: $this->trainingKey, scope: $this->trainingScope ?: null, allow: $this->trainingAllow ); } // Mark related requests as trained $this->matrix->markRequestsTrained( $entity, $this->trainingKey, $this->trainingScope ?: null ); $action = $this->trainingAllow ? 'allowed' : 'denied'; $lock = $this->trainingLock ? ' (locked)' : ''; session()->flash('message', "Permission '{$this->trainingKey}' {$action} for {$entity->name}{$lock}."); $this->closeTrainModal(); } catch (PermissionLockedException $e) { session()->flash('error', $e->getMessage()); } } public function bulkTrain(bool $allow): void { if (empty($this->selectedRequests)) { session()->flash('error', 'No requests selected.'); return; } $trained = 0; $errors = []; foreach ($this->selectedRequests as $requestId) { $request = PermissionRequest::find($requestId); if (! $request) { continue; } try { $entity = $request->entity; if (! $entity) { continue; } $this->matrix->train( entity: $entity, key: $request->action, scope: $request->scope, allow: $allow ); $this->matrix->markRequestsTrained( $entity, $request->action, $request->scope ); $trained++; } catch (PermissionLockedException $e) { $errors[] = $e->getMessage(); } } $this->selectedRequests = []; if ($errors) { session()->flash('error', implode(', ', $errors)); } $action = $allow ? 'allowed' : 'denied'; session()->flash('message', "{$trained} permissions {$action}."); } public function deletePermission(int $id): void { $permission = PermissionMatrix::findOrFail($id); if ($permission->locked) { session()->flash('error', 'Cannot delete a locked permission.'); return; } $permission->delete(); session()->flash('message', 'Permission deleted.'); } public function unlockPermission(int $id): void { $permission = PermissionMatrix::findOrFail($id); try { $this->matrix->unlock($permission->entity, $permission->key, $permission->scope); session()->flash('message', 'Permission unlocked.'); } catch (\Exception $e) { session()->flash('error', $e->getMessage()); } } public function closeTrainModal(): void { $this->showTrainModal = false; $this->trainingEntityId = null; $this->trainingKey = ''; $this->trainingScope = ''; $this->trainingAllow = true; $this->trainingLock = false; } public function render() { // Get pending requests $pendingRequests = PermissionRequest::query() ->with('entity') ->where('status', PermissionRequest::STATUS_PENDING) ->when($this->entityFilter, fn ($q) => $q->where('entity_id', $this->entityFilter)) ->when($this->search, fn ($q) => $q->where('action', 'like', "%{$this->search}%")) ->latest() ->paginate(20, ['*'], 'pending_page'); // Get trained permissions $permissions = PermissionMatrix::query() ->with('entity', 'setByEntity') ->when($this->entityFilter, fn ($q) => $q->where('entity_id', $this->entityFilter)) ->when($this->search, fn ($q) => $q->where('key', 'like', "%{$this->search}%")) ->when($this->statusFilter === 'allowed', fn ($q) => $q->where('allowed', true)) ->when($this->statusFilter === 'denied', fn ($q) => $q->where('allowed', false)) ->when($this->statusFilter === 'locked', fn ($q) => $q->where('locked', true)) ->orderBy('entity_id') ->orderBy('key') ->paginate(30, ['*'], 'permissions_page'); return view('commerce::admin.permission-matrix-manager', [ 'pendingRequests' => $pendingRequests, 'permissions' => $permissions, 'entities' => Entity::active()->orderBy('path')->get(), 'stats' => [ 'total_permissions' => PermissionMatrix::count(), 'allowed' => PermissionMatrix::where('allowed', true)->count(), 'denied' => PermissionMatrix::where('allowed', false)->count(), 'locked' => PermissionMatrix::where('locked', true)->count(), 'pending_requests' => PermissionRequest::where('status', PermissionRequest::STATUS_PENDING)->count(), ], ])->layout('hub::admin.layouts.app', ['title' => 'Permission Matrix']); } }