isDirectAdmin($user)) { return true; } $effect = $this->getEffectivePermissions($user)->get($permission); return $effect === 'allow'; } public function canAny(User $user, array $permissions): bool { foreach ($permissions as $permission) { if ($this->can($user, trim((string) $permission))) { return true; } } return false; } public function canViewField(User $user, string $module, string $field, ?string $entity = null): bool { return $this->can($user, $this->fieldPermissionSlug($module, $field, 'view')); } public function canUpdateField(User $user, string $module, string $field, ?string $entity = null): bool { return $this->can($user, $this->fieldPermissionSlug($module, $field, 'update')); } public function filterReadableFields(User $user, string $module, array $fields, ?string $entity = null): array { return array_values(array_filter( $fields, fn (string $field): bool => $this->canViewField($user, $module, $field, $entity) )); } public function filterWritableData(User $user, string $module, array $data, ?string $entity = null): array { return array_filter( $data, fn (string $field): bool => $this->canUpdateField($user, $module, $field, $entity), ARRAY_FILTER_USE_KEY ); } public function assertCan(User $user, string $permission): void { abort_unless($this->can($user, $permission), 403); } public function roleHasPermission(Role $role, string $permission): bool { if ($role->slug === Role::ADMIN) { return true; } $permissionModel = $role->permissions() ->where('slug', $permission) ->first(); return $permissionModel?->pivot?->effect === 'allow'; } public function getEffectivePermissions(User $user): Collection { if ($this->isDirectAdmin($user)) { try { return Permission::query()->pluck('slug')->mapWithKeys(fn (string $slug): array => [$slug => 'allow']); } catch (QueryException) { return collect(); } } return Cache::remember( $this->cacheKey($user), now()->addHour(), fn (): Collection => $this->resolveEffectivePermissions($user) ); } public function bumpCacheVersion(): void { Cache::forever(self::CACHE_VERSION_KEY, $this->cacheVersion() + 1); } private function resolveEffectivePermissions(User $user): Collection { if (!$this->tablesExist()) { return collect(); } $effects = collect(); $role = $user->roleModel()->with('permissions')->first(); if ($role) { foreach ($role->permissions as $permission) { $effects->put($permission->slug, $permission->pivot->effect); } } $userPermissions = $user->permissions() ->where(function ($query) { $query->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }) ->get(); foreach ($userPermissions as $permission) { $effects->put($permission->slug, $permission->pivot->effect); } return $effects; } private function isDirectAdmin(User $user): bool { return $user->resolvedRoleSlug() === Role::ADMIN; } private function fieldPermissionSlug(string $module, string $field, string $action): string { return "{$module}.fields.{$field}.{$action}"; } private function cacheKey(User $user): string { return 'permissions:user:' . $user->id . ':v' . $this->cacheVersion(); } private function cacheVersion(): int { return (int) Cache::get(self::CACHE_VERSION_KEY, 1); } private function tablesExist(): bool { return Schema::hasTable('roles') && Schema::hasTable('permissions') && Schema::hasTable('role_permissions') && Schema::hasTable('user_permissions'); } }