RestWS currently takes over $resource/% and assumes that existing menu links always follow the pattern "$resource/%$resource".
Among other things, this breaks comment permalinks, whose menu link is 'comment/%'. RestWS looks for 'comment/%' and if doesn't find that, it simply overrides it completely.
I am seriously wondering if it is a good idea to do this unconditionally for any entities. Sure, you could fix this special case for comments but there are many entities for which there is absolutely no reason to be *ever* exposed like this.
For example, private messages are entities in D7 and it's permission handling is *way* more complex than you could possibly handle out of the box in a general way. Someone is going to check the view permission and then the have a problem at hand :) And the possible (security) issues are limitless, just have a look at what restws is doing in my current (large) project:
mysql> select path, page_callback, page_arguments from menu_router where page_callback like 'restws%';
+--------------------------------+----------------------+---------------------------------------------------------+
| path | page_callback | page_arguments |
+--------------------------------+----------------------+---------------------------------------------------------+
| comment | restws_page_callback | a:1:{i:0;s:7:"comment";} |
| comment/% | restws_page_callback | a:1:{i:0;s:7:"comment";} |
| comment/%/view | restws_page_callback | a:1:{i:0;s:7:"comment";} |
| commerce_customer_profile | restws_page_callback | a:1:{i:0;s:25:"commerce_customer_profile";} |
| commerce_customer_profile/% | restws_page_callback | a:1:{i:0;s:25:"commerce_customer_profile";} |
| commerce_line_item | restws_page_callback | a:1:{i:0;s:18:"commerce_line_item";} |
| commerce_line_item/% | restws_page_callback | a:1:{i:0;s:18:"commerce_line_item";} |
| commerce_order | restws_page_callback | a:1:{i:0;s:14:"commerce_order";} |
| commerce_order/% | restws_page_callback | a:1:{i:0;s:14:"commerce_order";} |
| commerce_payment_transaction | restws_page_callback | a:1:{i:0;s:28:"commerce_payment_transaction";} |
| commerce_payment_transaction/% | restws_page_callback | a:1:{i:0;s:28:"commerce_payment_transaction";} |
| commerce_product | restws_page_callback | a:1:{i:0;s:16:"commerce_product";} |
| commerce_product/% | restws_page_callback | a:1:{i:0;s:16:"commerce_product";} |
| file | restws_page_callback | a:1:{i:0;s:4:"file";} |
| file/% | restws_page_callback | a:1:{i:0;s:4:"file";} |
| node | restws_page_callback | a:2:{i:0;s:4:"node";i:1;s:17:"node_page_default";} |
| node/% | restws_page_callback | a:3:{i:0;s:4:"node";i:1;s:14:"node_page_view";i:2;i:1;} |
| node/%/view | restws_page_callback | a:3:{i:0;s:4:"node";i:1;s:14:"node_page_view";i:2;i:1;} |
| redirect | restws_page_callback | a:1:{i:0;s:8:"redirect";} |
| redirect/% | restws_page_callback | a:1:{i:0;s:8:"redirect";} |
| rules_config | restws_page_callback | a:1:{i:0;s:12:"rules_config";} |
| rules_config/% | restws_page_callback | a:1:{i:0;s:12:"rules_config";} |
| search_api_index | restws_page_callback | a:1:{i:0;s:16:"search_api_index";} |
| search_api_index/% | restws_page_callback | a:1:{i:0;s:16:"search_api_index";} |
| search_api_server | restws_page_callback | a:1:{i:0;s:17:"search_api_server";} |
| search_api_server/% | restws_page_callback | a:1:{i:0;s:17:"search_api_server";} |
| taxonomy_term | restws_page_callback | a:1:{i:0;s:13:"taxonomy_term";} |
| taxonomy_term/% | restws_page_callback | a:1:{i:0;s:13:"taxonomy_term";} |
| taxonomy_vocabulary | restws_page_callback | a:1:{i:0;s:19:"taxonomy_vocabulary";} |
| taxonomy_vocabulary/% | restws_page_callback | a:1:{i:0;s:19:"taxonomy_vocabulary";} |
| user | restws_page_callback | a:2:{i:0;s:4:"user";i:1;s:9:"user_page";} |
| user/% | restws_page_callback | a:3:{i:0;s:4:"user";i:1;s:14:"user_view_page";i:2;i:1;} |
| user/%/view | restws_page_callback | a:3:{i:0;s:4:"user";i:1;s:14:"user_view_page";i:2;i:1;} |
| user/login | restws_page_callback | a:2:{i:0;s:4:"user";i:1;s:9:"user_page";} |
| wysiwyg_profile | restws_page_callback | a:1:{i:0;s:15:"wysiwyg_profile";} |
| wysiwyg_profile/% | restws_page_callback | a:1:{i:0;s:15:"wysiwyg_profile";} |
+--------------------------------+----------------------+---------------------------------------------------------+
What's the point of exposing stuff like rules configs, wysywig profiles, redirects, all that commerce stuff, search api configuration *out of the box*?
So why not do at least one of the following things, possibly both:
- Provide a simple list that allows to check of the resources/entities that you want to have exposed
- Use something like rest/$resource or maybe a configurable endpoint per entity/resource. Overriding menu callbacks of other modules like is just a mess IMHO and will always result in issues (like the currently open issue about the page manager conflict).
Sure this means that the "no configuration required" turns into a "minimal configuration required" but is that so bad? :)
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | restws-wildcard-loader.patch | 1.87 KB | klausi |
Comments
Comment #1
klausiYes, it is appealing to just don't do hook_menu_alter() at all. But this would violate the holy REST principles: a resource has one single canonical URL and nothing more. The path node/5 can return HTML, JSON, XML, RDF etc. If the JSON representation of a resources lives at another path it must be a different resource, but it isn't.
I find the idea of settings per entity type wrong just because Drupal URL/path/menu handling is not powerful enough. I will look into the menu_alter() and see if it can be fixed.
Comment #2
berdirYes, it would violate that principle. But it's broken anyway, isn't it? node/5 is not a resource, is a *page* that displays the node beneath many other things. node/5.json is *not* node/5 with a different format.
It is impossible for you to change that fact in Drupal 7. D8 might possibly be quite different here, but that's another story.
However, if you define that resources live at rest/resource/id, then *you* can return a real HTML representation of rest/node/5. And exactly the same thing at rest/node/5.json, just in a different format.
Comment #3
klausiFixed hook_menu_alter() to also address other wildcard patterns ('%' and '%resource' are supported now). Please test.
Comment #4
fagoI agree with berdir that exposing all the configuration entities makes not much sense. What about excluding them by checking for the 'configuration' flag in hook_entity_info()?
Also, do we already check whether the entity type supports entity_access() via entity_type_supports()? Once we do I don't see a potential security issue.
Comment #5
megadesk3000 commentedi can confirm that the patch in #3 works
Comment #6
berdirYes, the patch fixes the problem with comments.
Note that there are other issues, for example you expose taxonomy terms below taxonomy_term/% but taxonomy.module uses taxonomy/term/%. This isn't something that you can handle generically imho (e.g. you could replace _ with / here, but other entities might do something completely else.
For example, the URL for a private message is something like messages/%privatemsg_thread/#privatemsg-mid-$mid. So a message isn't even viewable on it's own, just within the thread the message belongs to. And a permalink feature similar to comments would be possible, but doesn't make much sense, as you usually don't share/link to private messages on a site anyway :)
IMHO, as a general rule, something that isn't exposed by the module that defines the entity (e.g. taxonomy terms are, but vocabularies aren't, so a vocabulary is kinda a configuration entity) should not be exposed by restws.module either (by default).
Comment #7
berdirDiscussed this with klausi in IRC, this what we agreed on:
- We add an optional 'restws_expose' property to entity_info. This is TRUE by default unless it's a configuration entity. By doing this, only non-configuration entities are exposed by default but this behavior can be overridden easily with hook_entity_info_alter() for both configuration and non-configuration entities, if you want to expose e.g. wysiwyg profiles.
- To solve the access issue (That is, who is responsible when restws exposed e.g. private messages which don't support entity access), we set the restrict access flag (and maybe a custom warning message) in the permission so that it is clear that users with the permission will have access to *all* entities of that type.
Comment #8
klausiCommitted patch from #3, see http://drupalcode.org/project/restws.git/commit/d2b3938
For the other stuff please open a new issue.
Comment #9
fagoImprovements sound good to me, though I'm not sure about the restrict flag. Having the rest-ws permission does in no way empower the user with administrative permission? On the contrary, rest-ws respects entity and field level access so I don't think it should be restricted.
I agree though that it should be clearly marked to apply to *any* entity of that type, e.g. it could take over the wording from the edit permissions.
Comment #10
berdirThe point is that not all entities correctly support entity access (yet) and because it's not a core feature, it's not clear whose fault that is. For example private messages. The restrict flag would *only* be set for those entities.
Comment #11
fago>The restrict flag would *only* be set for those entities.
I see! I'd be even more strict and disable support for them as users probably cannot know whether it's going to work or not. But at least doing the restrict flag makes sense here.
Comment #12
berdirThat's fine with me as well...