Problem/Motivation

#2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior recently made very important fixes to the core REST module. In doing so, it introduced an important change, which follows https://en.wikipedia.org/wiki/Robustness_principle.

It allows one to send values in PATCH requests, even for fields that the user is not allowed to modify, as long as they match the currently stored values.

This means developers can GET an entity, decode it from JSON into an object/array structure in JS, change a value, encode it again into JSON, and have it work. Today, they cannot do that: they have to explicitly omit fields that the user is not allowed to modify, otherwise they'll get a 403!

Proposed resolution

Implement the same change in JSON API.

Remaining tasks

  1. Roll patch, which should copy/paste the logic from core, plus #2938035: When PATCHing a field is disallowed, no reason is given for *why* this happens to ensure we still send the same excellent field access 403 responses that JSON API already does better than core REST!
  2. Wait for #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only to land.

User interface changes

None.

API changes

None.

Data model changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Wim Leers’s picture

FileSize
5.85 KB
e0ipso’s picture

Should we add a "warning" or an "error" in the response now that we have support for the Partial Success extension?

We could highlight the fields that would be un-editable, and that are recommended to be dropped from the payload.

Wim Leers’s picture

There's no need for any warning at all when sending unchanged values for fields. That means you don't intend to change anything. Receiving warnings for things that are fine and safe seems unnecessarily scary?

Wim Leers’s picture

In that same issue, we ran into #2824851-157: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior:

[…] UserResourceTestBase::testPatchDxForSecuritySensitiveBaseFields() REST tests are failing […]

This is because \Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraintValidator::validate() calls \Drupal\user\Entity\User::checkExistingPassword() which checks $user->get('pass')->existing … which … no longer gets set since #148 because it now only sets a value if it is different from the current value. Before, it was always being set.

And then in #2824851-194: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior:

[…]
Well … I actually investigated it a bit more, but still wanted to post that. The original reason for introducing that is at #157. And that reasoning was correct: the pass field was no longer being set for fields whose value hadn't changed.
But! Thanks to @cburschka's analysis, we ended up with a different implementation, which does always set the value. So actually, we didn't need the special casing of User's pass field anymore!

The patch in #3 also fixes this case: it ensures that User's pass field always gets set.

This issue should therefore also re-activate the test coverage that #2930028-54: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only is adding in \Drupal\Tests\jsonapi\Functional\UserTest::testPatchSecurityOtherUser()

Wim Leers’s picture

FileSize
5.96 KB

Rebased.

Wim Leers’s picture

Title: [PP-1] EntityResource::patchIndividual() should allow fields that the user is not allowed to change, as long as they match the current value » EntityResource::patchIndividual() should allow fields that the user is not allowed to change, as long as they match the current value
Assigned: Unassigned » Wim Leers
Issue summary: View changes
Status: Postponed » Active
Wim Leers’s picture

Status: Active » Needs review
FileSize
5.25 KB

Rebased #7 (had to fix some conflicts).

Status: Needs review » Needs work

The last submitted patch, 9: 2939810-9.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Wim Leers’s picture

That worked … for everything except File. Same exact failure on 8.4 and 8.6. Investigating…

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
3.37 KB
6.73 KB

That failed because jsonapi is adding a url computed field (which we'll deprecate in #2926463: [>=8.5] Remove JSON API's "file URL" field work-around now that Drupal core 8.5 fixed it), and the class corresponding to it does not compute it at all necessary times. This is similar to what we saw in Drupal core with Node's path field. To solve that, we did #2392845: Add a trait to standardize handling of computed item lists, which introduced a trait to standardize how computed field classes should behave, to prevent the same class of bugs from appearing everywhere.

In this case, it's failing because comparing the stored File entity's url field value with the value in the PATCH request concluded that the stored and received values were not the same. But they really are.

The solution is simple: updating \Drupal\jsonapi\Field\FileDownloadUrl to use the trait that #2392845 introduced.

Wim Leers’s picture

Assigned: Wim Leers » Unassigned

#12 is green on both 8.4 and 8.6!

gabesullice’s picture

Status: Needs review » Reviewed & tested by the community

Looks good. Super job watching out for the security pitfalls.

  • e0ipso committed 1eafaf8 on 8.x-1.x authored by Wim Leers
    Issue #2939810 by Wim Leers, e0ipso, gabesullice: EntityResource::...
e0ipso’s picture

There was a minor issue applying the patch. I think all went well after all.

 curl https://www.drupal.org/files/issues/2939810-12.patch | git apply -3
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  6890  100  6890    0     0  29880      0 --:--:-- --:--:-- --:--:-- 29826
error: patch failed: tests/src/Functional/ResourceTestBase.php:1181
Falling back to three-way merge...
Applied patch to 'tests/src/Functional/ResourceTestBase.php' cleanly.
e0ipso’s picture

Status: Reviewed & tested by the community » Fixed
Wim Leers’s picture

❤️🎉 — this was a very important one to land!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.