Follow-up to #2803179: [regression] rest_update_8201() can fail and break things

Steps to Reproduce

  1. Install the current stable version of Drupal (8.1.x)
  2. import the attached rest.settings.yml (or custom resource from parent issue)
  3. Upgrade to Drupal 8.2.x
  4. InvalidArgumentException: The method is not supported. in Drupal\rest\Entity\RestResourceConfig->normalizeRestMethod() (line 261 of [error]
    /.../core/modules/rest/src/Entity/RestResourceConfig.php).

Expected result

That custom rest resources would continue to work after upgrading to Drupal 8.2.x

Actual result

The update fails leaving the site half updated and broken.

Performing rest_update_8201                                                [ok]
Performing system_update_8200                                              [ok]
Performing system_update_8200                                              [ok]
Performing system_update_8200                                              [ok]
Performing system_update_8200                                              [ok]
Performing system_update_8201                                              [ok]
Performing rest_update_8202                                                [ok]
Performing system_update_8202                                              [ok]
Performing block_content_update_8003                                       [ok]
Performing comment_update_8200                                             [ok]
Performing path_update_8200                                                [ok]
Performing rest_update_8203                                                [ok]
Performing views_update_8200                                               [ok]
Cache rebuild complete.                                                    [ok]
Post updating block                                                        [ok]
Post updating contact                                                      [ok]
Post updating editor                                                       [ok]
InvalidArgumentException: The method is not supported. in Drupal\rest\Entity\RestResourceConfig->normalizeRestMethod() (line 261 of /home/jgilliland/public_html/d8/core/modules/rest/src/Entity/RestResourceConfig.php).
Post updating rest                                                         [ok]
Failed: InvalidArgumentException: The method is not supported. in Drupal\rest\Entity\RestResourceConfig->normalizeRestMethod() (line 261 of /home/jgilliland/public_html/d8/core/modules/rest/src/Entity/RestResourceConfig.php).
Cache rebuild complete.                                                    [ok]
Finished performing updates.                                               [ok]

Comments

neclimdul created an issue. See original summary.

neclimdul’s picture

Issue summary: View changes

Removing debug method bit from exception i added to figure out what was going on.

neclimdul’s picture

Issue summary: View changes

Making summary readable.

We implemented this hack because earlier in d8's release REST would do its method checks in hardcoded middleware that precluded contrib code from catching the core's request before rejecting it. By registering the method like this we could get further into the Symfony stack and handle the CORS OPTIONS request the same as other requests. I'm not sure if that's true but we have a different solution in place.

Since the previous reporter ran into a similar failure like this is seems we should make the update a little more resiliant to modified methods in the rest.settings.

dawehner’s picture

So the recommended action is to completely ignore unknown methods? That sounds like the option which at least doesn't break the update path.

wim leers’s picture

Title: [regression] rest_update_8201() can still fail and break things » REST in Drupal 8.2.x does not allow HTTP methods other than GET/PATCH/POST/DELETE: OPTIONS, PUT, et cetera all fail

So per #3, this is really about updating REST resource configuration that includes OPTIONS. #2803179-23: [regression] rest_update_8201() can fail and break things describes a similar problem with PUT.

Relevant quote:

The problem is that my custom rest resource implemented a PUT method, which is not valid in Drupal rest

This is a problem, and it makes no sense.

Since this code lives in \Drupal\rest\Entity\RestResourceConfig::normalizeRestMethod() and is called by both \Drupal\rest\Entity\RestResourceConfig::getAuthenticationProvidersForMethodGranularity() and \Drupal\rest\Entity\RestResourceConfig::getFormatsForMethodGranularity(), this is actually applying to not the upgrade path, but simply the behavior. It's possible to create a RestResourceConfig entity in a fresh Drupal 8.2.x site and also run into this problem.

The reason you're also running into it during the upgrade process, is because rest_post_update_resource_granularity() calls $resource_config_entity->save();, which is fine for a post-update function (post-update functions can rely on the entity system).

wim leers’s picture

Status: Active » Needs review
StatusFileSize
new1.17 KB

I think simply removing the validation makes most sense.

Note that this also matches the scope of what \Drupal\rest\Plugin\ResourceBase::requestMethods() allows:

 /**
   * Provides predefined HTTP request methods.
   *
   * Plugins can override this method to provide additional custom request
   * methods.
   *
   * @return array
   *   The list of allowed HTTP request method strings.
   */
  protected function requestMethods() {
    return array(
      'HEAD',
      'GET',
      'POST',
      'PUT',
      'DELETE',
      'TRACE',
      'OPTIONS',
      'CONNECT',
      'PATCH',
    );
  }

That lists all official HTTP methods. But allows a particular REST resource to define additional ones.

wim leers’s picture

Title: REST in Drupal 8.2.x does not allow HTTP methods other than GET/PATCH/POST/DELETE: OPTIONS, PUT, et cetera all fail » [regression] REST in Drupal 8.2.x does not allow HTTP methods other than GET/PATCH/POST/DELETE: OPTIONS, PUT, et cetera all fail
neclimdul’s picture

Status: Needs review » Reviewed & tested by the community

Worked swimmingly. I figured there was something that required it to be limited down to that set of methods but tests don't seem to think so and this implementation makes more sense to me. We didn't even seem to assert the validation :-D

Thanks!

wim leers’s picture

We didn't even seem to assert the validation :-D

Indeed!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

I think we should have a test for this. We might decide to limit this again in the future and be wrong.

alexpott’s picture

Issue tags: +Triaged D8 critical

Discussed with @effulgentsia, @catch, @xjm and @cilefen. We agreed this was critical because if this update fails your rest settings and not upgraded to work with 8.2.x and your site is then broken.

xjm’s picture

Issue tags: +D8 upgrade path
mradcliffe’s picture

Status: Needs work » Needs review
StatusFileSize
new1.51 KB
new2.56 KB
new1.34 KB

Here is a start to an unit test. I added as a pure unit test for speed. It invokes the getMethods method on the entity, which seems to be the easiest way to get at the normalizeRestMethod protected method.

I thought about adding a data provider, but that really could be done in a follow-up for testing getMethods, getFormats, etc... respectively.

The last submitted patch, 13: 2828319-test-only-13.patch, failed testing.

dawehner’s picture

+++ b/core/modules/rest/src/Entity/RestResourceConfig.php
@@ -243,24 +243,16 @@ protected function getRestResourceDependencies() {
-    if (!in_array($normalised_method, $valid_methods)) {
-      throw new \InvalidArgumentException('The method is not supported.');
-    }

Ideally our entity plugin could somehow validate this

wim leers’s picture

#15: that's already happening. See \Drupal\rest\Plugin\rest\resource\EntityResource::availableMethods(), which calls \Drupal\rest\Plugin\ResourceBase::availableMethods(), which calls \Drupal\rest\Plugin\ResourceBase::requestMethods().

So, it's being validated not at configuration time, but at the run time.

Ideally, the configuration itself could indeed be validated. But then we need a configuration validation API, which doesn't exist … (#2164373: [META] Untie config validation from form validation — enables validatable Recipes, decoupled admin UIs …)

wim leers’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests
+++ b/core/modules/rest/tests/src/Unit/Entity/RestResourceConfigTest.php
@@ -0,0 +1,42 @@
+   * This also tests that no exceptions are thrown during that method so that
+   * alternate methods such as OPTIONS and PUT are supported.
+   */
+  public function testNormalizeRestMethod() {
+    $expected = ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS'];
+    $methods = ['get', 'put', 'post', 'patch', 'delete', 'options'];

I think this should also do a "foo" method, just to show that it literally doesn't care about which HTTP methods are being used.

Once that's done, this is ready IMO.

mradcliffe’s picture

Status: Needs work » Needs review
StatusFileSize
new2.57 KB
new821 bytes

Adds change to test for adding an unsupported method to assert that normalizeRestMethod should not be doing validation from #17.

I thought about adding a comment, but I wasn't sure about the wording so I left it without.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 2828319-18.patch, failed testing.

mradcliffe’s picture

Status: Needs work » Reviewed & tested by the community
wim leers’s picture

Retesting against 8.2.x, and adding a test against 8.3.x. Because #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method just landed.

  • catch committed 6e26b86 on 8.3.x
    Issue #2828319 by mradcliffe, Wim Leers, neclimdul: [regression] REST in...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.3.x and cherry-picked to 8.2.x. Thanks!

  • catch committed 3b8e52a on 8.2.x
    Issue #2828319 by mradcliffe, Wim Leers, neclimdul: [regression] REST in...

Status: Fixed » Closed (fixed)

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