Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Laravel: Disabling Pagination Causes 403 on Policy-Protected Collection Endpoints

已关闭
#8,482 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
72/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
laravel, php

调研方向

从 CollectionProvider.php 和 ResourceAccessChecker.php 开始,然后检查访问拒绝路径中的 AccessCheckerProvider.php。使用 viewAny() 返回 true 的 policy,分别在启用和禁用分页的情况下重现这两个 collection 请求。完成标准是:受 policy 保护的 collection 端点不再仅仅因为分页被禁用而返回 403,并为 pagination=false 和 pagination=0 都提供回归覆盖。

由索引模型根据 Issue 内容生成。

描述

API Platform version(s) affected: 4.3.17 (api-platform/laravel)

Description

Any GetCollection operation that has a Policy/security expression (resolved via a Laravel Policy's viewAny() method, e.g. through the standard Gate::getPolicyFor($model) naming-convention resolution) unconditionally returns 403 Access Denied when the request disables client-side pagination (?pagination=false or ?pagination=0), regardless of authentication state or the policy's actual logic — even when viewAny() unconditionally returns true.

Root cause: ApiPlatform\Laravel\Security\ResourceAccessChecker::isGranted() special-cases the Paginator wrapper class when deciding what object to pass to Gate::allows():

public function isGranted(string $resourceClass, string $expression, array $extraVariables = []): bool
{
    $object = $extraVariables['object'] ?? null;

    return Gate::allows(
        $expression,
        ($object instanceof Paginator || null === $object) ? $resourceClass : $object
    );
}

When pagination is enabled, ApiPlatform\Laravel\Eloquent\State\CollectionProvider::provide() returns an ApiPlatform\Laravel\Eloquent\Paginator instance, so $object instanceof Paginator is true, and Gate::allows($expression, $resourceClass) is called with the resource class string — which correctly resolves to the model's Policy class and calls viewAny().

When pagination is disabled, the same provider instead returns a plain Illuminate\Database\Eloquent\Collection (see CollectionProvider::provide(), the false === $this->pagination->isEnabled(...) branch: return $query->get();). This is not a Paginator, so ResourceAccessChecker passes the Collection object itself to Gate::allows($expression, $collection). Laravel's Gate then tries to resolve a policy based on get_class($collection) (Illuminate\Database\Eloquent\Collection), finds no policy registered for that class, and — absent any other applicable ability/Gate::before() override — denies the request by default.

The result: disabling pagination on any policy-protected collection endpoint always 403s, independent of the actual authorization rule.

How to reproduce

Any Eloquent resource with a Policy providing viewAny():

class PaymentMethodPolicy
{
    public function viewAny(?RemoteUser $user): bool
    {
        return true; // unconditionally public
    }
}
GET /api/payment_methods
→ 200 OK (Gate::allows('viewAny', PaymentMethod::class) — resolves the policy correctly)

GET /api/payment_methods?pagination=false
→ 403 {"detail":"Access Denied."}
(reproduces identically authenticated or unauthenticated)

GET /api/payment_methods?pagination=0
→ 403 (same)

Possible Solution

ResourceAccessChecker::isGranted() should pass the resource class string whenever $object is a bare collection of resources (i.e., not a single instance of the resource itself) rather than only special-casing Paginator. For example, also treat PartialPaginator and plain Illuminate\Support\Collection/Illuminate\Database\Eloquent\Collection instances the same way Paginator is treated:

$isCollectionLike = $object instanceof Paginator
    || $object instanceof PartialPaginator
    || $object instanceof \Illuminate\Support\Collection;

return Gate::allows(
    $expression,
    ($isCollectionLike || null === $object) ? $resourceClass : $object
);

Additional Context

  • CollectionProvider: vendor/api-platform/laravel/Eloquent/State/CollectionProvider.php
  • ResourceAccessChecker: vendor/api-platform/laravel/Security/ResourceAccessChecker.php
  • AccessCheckerProvider (throw site): vendor/api-platform/laravel/State/AccessCheckerProvider.php
  • This was found while debugging a checkout page that couldn't load any payment methods; it was initially conflated with an unrelated BooleanFilter validation bug (see docs/bug-reports/api-platform-boolean-filter-validation.md) because both bugs happened to be triggered by parameters on the same request (?enabled=true&pagination=false) and both produce failure responses that look superficially similar during triage (422 vs 403) unless isolated parameter-by-parameter.
  • Workaround applied in our app: stopped disabling client-side pagination on this endpoint (removed pagination: false from the frontend query) — the resource's default page size (20) is more than sufficient for a payment-methods list, so no functional loss.
主要语言
PHP
星标
2.6k
派生
987
平均合并
1 天 10 小时
30 天内合并 PR
76

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

api-platform/core 的其他 Issue

查看 api-platform/core 的全部 Issue

相似的 Issue

更多 PHP Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。