JSON:API collection filters return empty for non-administrator users since farmOS 4.0.5 (Entity API 1.8)

Since farmOS 4.0.5, every filter[...] on asset, log and quantity collections through JSON:API returns HTTP 200 with an empty data array for an OAuth2 user holding the Farm Manager role, whatever the filter says. Unfiltered reads, sort and page[offset] work; an unknown field still returns a 400. Taxonomy term and file collections filter normally. Reproduced on 2026-09-22 against our instance (4.0.5) with an exact-name filter on a land asset that an unfiltered read returns one request earlier, and with a not-equal filter that should match every row.

Cause, as far as we can trace it: farmOS 4.0.5 updates the Entity API module to 1.8 (SA-CONTRIB-2026-113). Its commit c07395aa (“fix: Update JSONAPI filtering”) removes the branch in entity_jsonapi_entity_filter_access() that granted JSONAPI_FILTER_AMONG_ALL when the query-access handler returned no conditions. Assets, logs and quantities use Entity API’s query access handler; for a manager the conditions are empty (the account has view any asset), so the hook now returns nothing, core’s generic rule then requires the entity type’s admin permission (administer assets, administer log, administer quantity), the managed roles do not hold it, and TemporaryQueryGuard appends alwaysFalse().

Effect: any integration that filters assets or logs through JSON:API with a manager, worker or viewer token stops seeing data, silently. Our MCP server, and any farmOS client using filter[name] to resolve an asset before a write, is affected.

Possible fixes upstream: implement hook_jsonapi_entity_filter_access() for asset, log and quantity in farmOS, granting JSONAPI_FILTER_AMONG_ALL to accounts with the view any <type>permission (mirroring what Entity API 1.7 did), or add the admin permissions to the managed roles’ “view all” setting.

We have worked around it client-side (unfiltered paginated reads, matched in memory). Happy to test a fix.

2 Likes

Thanks for reporting this @agnes! I’m surprised our tests didn’t catch it.

The Entity API module’s release notes hinted at this:

This release changes JSONAPI entity collection access control. In certain circumstances this may mean that JSONAPI clients can access less data than expected. Sites should carefully review their JSONAPI integrations and implement their own json api filter access rules if necessary. See examples in jsonapi module in core on how to do so for your own entity types: https://git.drupalcode.org/project/drupal/-/blob/main/core/modules/jsona…

Client Challenge

I saw this, but assumed that we were OK because our tests passed. :roll_eyes: :face_exhaling: :person_shrugging:

This looks like the right approach, once we have some tests to replicate it…

Working on a fix here: Fix JSON:API entity filter access by mstenta · Pull Request #1104 · farmOS/farmOS · GitHub

1 Like

Awesome Mike, thank you for quick followup . Much appreciated :folded_hands:

The fix was relatively simple! Updating our automated tests was less simple. :sweat_smile:

All done now. PR is ready for review!

1 Like

The PR has been merged into 4.x. I have deployed the fix to Farmier-hosted instances.

@agnes Please test and confirm it is working as expected now.

I will backport this to 4.0.x and tag a new 4.0.7 release soon.

Confirmed working — thank you, that was fast.

I re-ran the same checks from the original report against our instance (margregen.farmos.net) this morning, authenticated by OAuth2 password grant with the farm_manager scope, the same way our integration connects. Every filter that returned an empty list before now returns the right rows.

What I tried Before Now
/api/asset/land?filter[name]=Paddock 2 0 1
filter[name][value]=…&filter[name][operator]== 0 1
filter[name][condition][path]=name&…[value]=… 0 1
/api/asset/plant?filter[name]=20 MAR 2025 - Comfrey - P2R1.0-3 0 1
/api/log/activity?filter[name]=Note — NURS.GR 0 1
/api/asset/plant?filter[archived]=0 / =1 0 / 0 5 / 5
/api/asset/land?filter[bogusfield]=x 400 400 (still correctly refused)

I also checked the patterns our own integration actually depends on, which the original report did not cover, since those are what broke in practice:

What I tried Now
filter[name][operator]=CONTAINS on plant names (species search) 5 rows
filter[name][operator]=CONTAINS on land names (section search) 5 rows
/api/log/activity?filter[location.id]=… 4 rows
/api/log/activity?filter[status]=pending 5 rows
/api/quantity/standard?filter[measure]=count 3 rows
/api/quantity/standard?filter[inventory_asset.id]=… 5 rows
/api/asset/plant?filter[plant_type.name]=Comfrey 5 rows

That last one is worth flagging back to you: the nested relationship filter works against real data, so the refactor you did after the discussion with @paul121 and @symbioquine holds up outside the test suite.

Thanks again for turning this round so quickly, and for the extra test coverage in the OAuth2 token context — that is the context we actually run in, so it matters to us more than the default one.

2 Likes

Thanks for the detailed feedback @agnes.

I have backported this fix to 4.0.x and just tagged 4.0.7. It should be available soon.