> */ public function catalog(): array { $programs = config('program_acl.programs', []); return is_array($programs) ? $programs : []; } /** @return array */ public function programOptions(): array { $options = []; foreach ($this->catalog() as $key => $definition) { $label = trim((string) ($definition['label'] ?? $key)); $description = trim((string) ($definition['description'] ?? '')); $options[$key] = $description !== '' ? ($label . ' - ' . $description) : $label; } return $options; } /** @return array */ public function manageableRolesFor(User $actor): array { if ($actor->hasRole('super-admin')) { return ['admin', 'amministratore', 'collaboratore', 'fornitore', 'condomino', 'inquilino', 'proprietario']; } if ($actor->hasRole('admin')) { return ['amministratore', 'collaboratore', 'fornitore', 'condomino', 'inquilino', 'proprietario']; } if ($actor->hasRole('amministratore')) { return ['collaboratore']; } return []; } public function canManageRole(User $actor, string $role): bool { return in_array($role, $this->manageableRolesFor($actor), true); } public function manageableUsersQuery(User $actor): Builder { $query = User::query()->with('roles')->orderBy('name'); if ($actor->hasAnyRole(['super-admin', 'admin'])) { return $query; } if (! $actor->hasRole('amministratore')) { return $query->whereRaw('1 = 0'); } if (! Schema::hasColumn('users', 'amministratore_id')) { return $query->whereRaw('1 = 0'); } $amministratoreId = (int) ($actor->amministratore?->id ?? 0); if ($amministratoreId <= 0) { return $query->whereRaw('1 = 0'); } return $query ->where('amministratore_id', $amministratoreId) ->whereHas('roles', static function (Builder $roleQuery): void { $roleQuery->where('name', 'collaboratore'); }); } /** @return array */ public function manageableUserOptions(User $actor): array { return $this->manageableUsersQuery($actor) ->get(['id', 'name', 'email']) ->mapWithKeys(static function (User $user): array { $label = trim((string) $user->name); $email = trim((string) $user->email); if ($label === '') { $label = $email !== '' ? $email : ('Utente #' . (string) $user->id); } elseif ($email !== '') { $label .= ' <' . $email . '>'; } return [(int) $user->id => $label]; }) ->all(); } public function canManageUser(User $actor, User $target): bool { if ($actor->hasAnyRole(['super-admin', 'admin'])) { return true; } if (! $actor->hasRole('amministratore')) { return false; } if (! $target->hasRole('collaboratore') || ! Schema::hasColumn('users', 'amministratore_id')) { return false; } return (int) $target->amministratore_id === (int) ($actor->amministratore?->id ?? 0); } /** @return array{allow: array, deny: array} */ public function roleRules(string $role): array { if (! $this->hasRulesTable()) { return ['allow' => [], 'deny' => []]; } $rules = ProgramAclRule::query() ->forRole($role) ->get(['program_key', 'is_allowed']); return [ 'allow' => $rules->where('is_allowed', true)->pluck('program_key')->values()->all(), 'deny' => $rules->where('is_allowed', false)->pluck('program_key')->values()->all(), ]; } /** @return array{allow: array, deny: array} */ public function userRules(int $userId): array { if (! $this->hasRulesTable()) { return ['allow' => [], 'deny' => []]; } $rules = ProgramAclRule::query() ->forUser($userId) ->get(['program_key', 'is_allowed']); return [ 'allow' => $rules->where('is_allowed', true)->pluck('program_key')->values()->all(), 'deny' => $rules->where('is_allowed', false)->pluck('program_key')->values()->all(), ]; } /** @param array $allow * @param array $deny */ public function replaceRoleRules(string $role, array $allow, array $deny): void { if (! $this->hasRulesTable()) { return; } ProgramAclRule::query()->forRole($role)->delete(); $this->insertRules('role', $role, $allow, $deny); } /** @param array $allow * @param array $deny */ public function replaceUserRules(int $userId, array $allow, array $deny): void { if (! $this->hasRulesTable()) { return; } ProgramAclRule::query()->forUser($userId)->delete(); $this->insertRules('user', (string) $userId, $allow, $deny); } public function canAccessProgram(?User $user, string $programKey): bool { $catalog = $this->catalog(); if (! isset($catalog[$programKey]) || ! $user instanceof User) { return false; } if ($user->hasRole('super-admin')) { return true; } $roleNames = $user->getRoleNames()->values()->all(); if ($this->hasRulesTable()) { $userRule = ProgramAclRule::query() ->forUser((int) $user->id) ->where('program_key', $programKey) ->first(); if ($userRule instanceof ProgramAclRule) { return (bool) $userRule->is_allowed; } if ($roleNames !== []) { $roleRules = ProgramAclRule::query() ->where('scope_type', 'role') ->whereIn('scope_key', $roleNames) ->where('program_key', $programKey) ->get(['is_allowed']); if ($roleRules->contains(static fn(ProgramAclRule $rule): bool => $rule->is_allowed === false)) { return false; } if ($roleRules->contains(static fn(ProgramAclRule $rule): bool => $rule->is_allowed === true)) { return true; } } } $defaultRoles = $catalog[$programKey]['default_roles'] ?? []; if (! is_array($defaultRoles)) { return false; } foreach ($roleNames as $roleName) { if (in_array($roleName, $defaultRoles, true)) { return true; } } return false; } /** @param array $allow * @param array $deny */ private function insertRules(string $scopeType, string $scopeKey, array $allow, array $deny): void { if (! $this->hasRulesTable()) { return; } $catalogKeys = array_keys($this->catalog()); $allow = array_values(array_unique(array_intersect($catalogKeys, $allow))); $deny = array_values(array_unique(array_diff(array_intersect($catalogKeys, $deny), $allow))); $rows = []; foreach ($allow as $programKey) { $rows[] = [ 'scope_type' => $scopeType, 'scope_key' => $scopeKey, 'program_key' => $programKey, 'is_allowed' => true, 'created_at' => now(), 'updated_at' => now(), ]; } foreach ($deny as $programKey) { $rows[] = [ 'scope_type' => $scopeType, 'scope_key' => $scopeKey, 'program_key' => $programKey, 'is_allowed' => false, 'created_at' => now(), 'updated_at' => now(), ]; } if ($rows !== []) { ProgramAclRule::query()->insert($rows); } } private function hasRulesTable(): bool { return Schema::hasTable('program_acl_rules'); } }