Problem/Motivation
Working on #2291055: REST resources for anonymous users: register I have detected that I cannot create user entities. When I try it the response is {"error":"Access denied on creating field pass"}.
Proposed resolution
As @Berdir proposed at #2291055: REST resources for anonymous users: register the usage of create here is not correct.
foreach ($entity as $field_name => $field) {
if (!$field->access('create')) {
throw new AccessDeniedHttpException(String::format('Access denied on creating field @field', array('@field' => $field_name)));
}
}
"CreateTest" covers node and entity_test but we should cover the user entity too.
Comment | File | Size | Author |
---|---|---|---|
#53 | 2405091-53.patch | 26.13 KB | marthinal |
#53 | interdiff-2405091-51-53.txt | 1.29 KB | marthinal |
#51 | 2405091-51.patch | 26.1 KB | marthinal |
#51 | interdiff-2405091-49-51.txt | 2.78 KB | marthinal |
#49 | 2405091-49.patch | 25.42 KB | marthinal |
Comments
Comment #1
marthinal CreditAttribution: marthinal commentedA quick update. It's not possible to use edit for "changed" field.
ChangedFieldItemList::defaultAccess
Comment #2
BerdirThat is correct, it is not possible to write to changed.
Comment #3
marthinal CreditAttribution: marthinal commentedAs discussed in IRC with @Berdir, "changed" never should be checked. We only need to check fields where we actually post some data.
Comment #4
marthinal CreditAttribution: marthinal commentedFor 'patch' we are only checking fields added to the hal+json. As @Berdir suggested me, we can use _restPatchFields and probably it's good idea to rename it.
Let's try then!
Comment #5
marthinal CreditAttribution: marthinal commentedComment #6
BerdirYou need to remove the request_method check then, because a create will be a post, not a patch.
This check used to be necessary when we were messing with the entity object in weird ways. We no longer do, which means we can remove the check, and decide in EntityResource when and how to use it.
Comment #7
marthinal CreditAttribution: marthinal commentedSure! Thanks @Berdir :)
Comment #11
Berdir_restSubmittedFields is just a list of field names, which you need to call as $entity->get($field_name)->access()
Comment #12
marthinal CreditAttribution: marthinal commentedThanks @Berdir!
So let's try this!
Comment #14
marthinal CreditAttribution: marthinal commentedLooks like we have access problems creating a node "Response body: {"error":"Access denied on creating field status"}"
Comment #15
Berdirstatus requires administer nodes permission I think, see fieldAccess() in NodeAccessControlHandler. Try to remove that or give the user that permission.
Comment #16
BerdirAlso, this is a security issue, because it means we have no/invalid access handling for creating new content.
Comment #17
marthinal CreditAttribution: marthinal commentedI prefer to create the content from a non-admin user... Anyways we can test both but I'm not sure if we need it...
Comment #18
catchYes this is bad per #16, marking as triaged.
Comment #19
BerdirTrailing spaces here.
Yes, I think it would be useful to have a second test as a user with administer nodes permission, to ensure that we can then write those.
And we need a third test, to verify that it is not allowed to write e.g. the node status as a normal user. One field as an example should be enough to verify that the glue code between rest and field access is working.
And of course a test for creating a user :)
Comment #20
marthinal CreditAttribution: marthinal commentedComment #21
clemens.tolboomThe issue summary states
How is this patch accomplish that.
whitespace
Comment #22
marthinal CreditAttribution: marthinal commentedTest that creates entities as non-admin user + admin user + covering user entity creation.
Comment #23
BerdirLet's add a comment here, that explains that we only want to check edit permissions for fields that were actually submitted by the user. Maybe also mention that field access makes no difference between between create and update, so the edit operation is used here.
This test is getting pretty hard to read, with all those different cases. Maybe we can find a different way to structure it, a separate method per entity type and some helper methods for he parts that are actually the same?
As this is specific to nodes, maybe we should name it $node and type hint on NodeInterface.
unset() on entity fields is a bit strange, I suggest you use $entity->status = NULL or maybe array() instead, if that works. Maybe check with @yched.
Comment #24
marthinal CreditAttribution: marthinal commentedMissing user entity test. It's commented at this moment (public function _testCreateUser()). I can only create user entities as admin using basic auth and not sure why... the last test at #22 passed with the same code. Once this test is fixed then I need to add the only test .patch
Comment #25
marthinal CreditAttribution: marthinal commentedSorry @Berdir I forgot to explain a little bit better :)
1) Done.
2) Done.
3) Done
Missing to remove the unset().
Comment #26
larowlan>80 chars
Will try and take a look at the user tests later in the week
Comment #27
marthinal CreditAttribution: marthinal commentedLet's check the test results for testCreateUser().
Comment #29
marthinal CreditAttribution: marthinal commentedFrom my Rest Client I have the same problem for the user entity creation and I can fix the problem using the token (X-CSRF-Token request header). For the node creation the result should be the same but I'm not sure why the test works as expected in this case...
Comment #30
marthinal CreditAttribution: marthinal commentedThe test fails because a user with only 'administer users' permission cannot create user entities from rest. From the UI I can create user entities with the same user... The admin user(uid 1) can create user entities from rest.
And here is the problem (EntityResource::post) :
Maybe I'm missing something...
Comment #31
larowlanTagging in the hope someone comes to critical office hours interested in this
Comment #32
BerdirAwesome, you do keep finding bugs :)
This is as simple as stupid.
The user entity annotation has:
That is now how this permission is called :)
That said, there were also a few things wrong in the test. You checked that access is denied for non-administer users, but then still continued to call createEntityOverRestApi(), which obviously failed :)
Also, createEntityWithoutProperPermissions() did not work for users (because there *are* users), and it is not necessary, so removed that.
The test is certainly already better to understand, still thinking about how to improve it. Not sure yet. Also don't want to delay fixing those critical bugs because of imperfect tests :) That said, comments and coding standards (docblocks, comment lengths and such) certainly need some work.
Comment #33
marthinal CreditAttribution: marthinal commentedoh I see! Didn't know about this change. :)
Sure! I will work later today to improve the comments at least.
Comment #34
marthinal CreditAttribution: marthinal commentedOk let's try it :)
I have a couple of questions:
1) Not sure if I should use '\Drupal::entityManager()->getStorage($entity_type)->create($values)' instead of entity_create();
2) it is very helpful to add this comment about CSRF token but not sure if we want it here :)
Comment #35
marthinal CreditAttribution: marthinal commentedAbout 1), some functions are deprecated but probably in this case makes no sense at all :)
Comment #36
BerdirAdding more comments is good, but what I meant is coding standards for e.g. @param, with a type and description, correctly type hint on EntityInterface and so on.
1. Yes, I suggest you use the storage handler if you don't know the entity class (e.g. Node::create() works too, but only if you know that you are working wit nodes).
Comment #37
marthinal CreditAttribution: marthinal commentedImproving comments and coding standards. Using storage handler when I don't know the entity class and using the static create() method when I know the entity class.
Comment #39
RavindraSingh CreditAttribution: RavindraSingh commentedSeems good to me, Just adding t() for notices.
Comment #40
dawehner-1 to use t() in exceptions IMHO
... We don't translate logging messages
Alright, let's get rid of this line :)
It feels like a hack / implementation detail, that this is even possible.
Comment #41
marthinal CreditAttribution: marthinal commented@RavindraSingh Looks like we don't need any translation here.
@dawhener
3) Done.
4) Not sure when exactly this method could break something... I was reviewing how the magic method __unset() works and looks ok. Also I don't know the best way to remove these fields then. I think a method is probably the best way to avoid repeating the same code ...
Comment #42
dawehnerIts not about needing, its about making things translated which shouldn't, as things are broken, so t() causes even potentially harm.
Comment #43
mikey_p CreditAttribution: mikey_p commentedmissing space after user.
Might want to rename or change the accepted parameters. It's not clear why this isn't user later where it's using httpRequest(). Maybe something like:
Might want to rename this make it clear that this method deletes the entity.
Not sure if this is too pedantic, but I would expect any helper method that has assertions in it to start with assert, i.e. assertCreateEntityInvalidData(). Is there a core convention for this?
Comment #44
marthinal CreditAttribution: marthinal commented1) Done.
2) Not sure we need it...
3) We are removing a loaded entity from db.
4) Done. I'm not sure it's 100% correct but makes sense to me. Anyways needs review.
Thanks for reviewing the issue!!!
Comment #45
mikey_p CreditAttribution: mikey_p commentedLooks good to me.
Comment #46
alexpottComment #47
catchOverall looks good. Found some nitpicks.
Can skip 'want to'.
Is there an issue for this?
Should be one line.
s/where/that/
Or I'd reword to 'Remove node fields that can only be written by an admin user'.
Comment #48
Berdir2. That comment is just moved in the patch. However, it looks weird to me, we supported entity reference fields for a very, very long time? maybe we can just remove that unset() ?
Comment #49
marthinal CreditAttribution: marthinal commented#47
1) Done.
2) Done.
3) Done.
4) Done.
Thanks!
Comment #50
BerdirNeither version is correct.
The first sentence must not be more than 80 characters.
Two ways to fix it:
1. Keep the old version, but add an empty line inbetween.
2. Rewrite into a single, shorter sentence. I'd prefer that. Something like "Send an invalid UUID to trigger the entity validation." ?
classes/interfaces in @param/@return should always use the full namespace, so \Drupal\Core\Entity\EntityInterface.
Also namespace missing and missing description for @param and possibly @return (not visible from context)
Comment #51
marthinal CreditAttribution: marthinal commented@Berdir Done!
Comment #52
BerdirNot sure if we need to mention curl_exec() here, I'd just say "... from the request." ? Also, this should be a string, not a boolean? (which would be "bool").
I agree that the unset business is spooky. I think it would be better to set them to NULL/empty array, that should have the same effect. But that unset stuff seems to be an existing thing in rest test, so probably better to clean it up in a separate issue, this is big enough and solves the critical part.
Comment #53
marthinal CreditAttribution: marthinal commented@Berdir
Yes, it was a string, sorry.
Comment #54
BerdirThanks, back to RTBC.
Comment #55
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 466759d and pushed to 8.0.x. Thanks!
Comment #57
catchI'd started reviewing this but only found comments over 80 chars. Opened #2418559: Fix comment formatting in /core/modules/rest/src/Tests/CreateTest.php as very minor follow-up.
Comment #58
marthinal CreditAttribution: marthinal commentedOpened a new one(#2418587: Set entity values to NULL instead of using unset() method: unset() is misleading) to assign NULL instead of using unset(). (#52)