-
-
Notifications
You must be signed in to change notification settings - Fork 824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(WIP) CRM-20210: Improve authorization check for dynamic fks #10010
(WIP) CRM-20210: Improve authorization check for dynamic fks #10010
Conversation
Instead of actually calling the delegated action in order to check if it's authorized or not, we now use Kernel->runAuthorize() to do it. The way the check worked previously was: In order to be able to add an attachment to a entity X, the user must have create permissions to X. DynamicFKAuthorization would then call X.create passing the ID of the entity to which we want to add the attachment to. If the API call fails, it means the authorization failed. Otherwise, the attachment creation would proceed. The problem with this approach is that the API can fail for different reasons other than Authorization. For example, a custom API can fail because it was expecting other mandatory fields (besides the id given by DynamicFKAuthorization). By using Kernel->runAuthorize(), we avoid this problem, since now the actual action will not be executed. Since now we don't need to update the DB in order to check for Authorization, the transaction isn't necessary anymore.
Can one of the admins verify this patch? |
Theory looks good, although I would want @colemanw to review this. @ErichBSchulz fyi |
I think this is right, but would like @totten to review since he is the one who originally wrote this fn. |
@civicrm-builder add to whitelist |
thanks for the pointer @eileenmcnaughton - my question:answer ratio is gradually falling but it's quite a mountain :-/ |
@davialexandre in a proper and consistent world, I would agree with this revision. But we're not in a proper and consistent world. :( For example, consider the Contact API which begins: function civicrm_api3_contact_create($params) {
$contactID = CRM_Utils_Array::value('contact_id', $params, CRM_Utils_Array::value('id', $params));
if ($contactID && !empty($params['check_permissions']) && !CRM_Contact_BAO_Contact_Permission::allow($contactID, CRM_Core_Permission::EDIT)) {
throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record');
}
...
} There's an authorization check in the body of the API method. This authorization-check is not fired as a listener in the The There are a few ways to address this:
|
If I were in your shoes, I'd go with Aside: There had been a question before about why (In effect, the story of the Civi API is that it started as the wild-west and gradually introduced law-and-order. |
@totten So, my plan would be:
Does that sound like a good plan? A note about how did we find this and updating only the ID in APIsWe found this problem while trying to add attachments to a LeaveRequest in CiviHR. We have some quite complex validations done when creating or updating a leave request. One of these (the very first one) is checking for required fields. We could probably not check for these during an update, but there are a few problems with this:
So, even though it would not be dangerous to update a LeaveRequest passing only the ID (it will not change anything after all), the validation we have will prevent that |
OK, so drilling down on The intent is to allow more entities in
I guess I'm nervous about opening up to all entities in core+contrib. What would you think of keeping the whitelist -- but allowing contrib to alter the whitelist? For example, https://gist.github.com/totten/9e4cbc26ba33522c33e420728ef5bff4 provides pseudocode. I like that approach because it doesn't require any new hooks/events (but also willing to talk about other approaches if they seem clearer in your mind). Some kind of API metadata approach might also be reasonable -- and possibly favored by @colemanw or @eileenmcnaughton -- but it's less clear in my mind where to put that API metadata. (I personally feel less design-ambiguity in the |
I think it's fine to continue putting discussion here, but I'm flagging this WIP because the discussion in #10010 indicates that we need major changes. If you'd rather close/reopen, that's also valid/appreciated. |
Just as a general admin thing - I do think it's good to close PRs when they are not progressing & re-open then when ready to progress again. I'm pretty sure you can still comment & discuss on closed PRs. |
@totten sorry the delay to reply to this. I was just checking your gist and I really like the idea of opening
We can have a default implementation that works as it is today (calling the delegated API) and another one to use Besides passing the entity and action to the authorizer, we can also pass the params. This would given even more flexibility and allow more complex logic like users being able to add attachments to an entities not created by them. Does this sound good to you? |
hey @totten, any thoughts about my last comment here? |
I'm going to close this because it's a year old & not really moving / resolved / mergeable. Conversation can still proceed about this on a closed PR & re-opening a PR once it is ready will bring it back to the top of the queue. |
(note it's still tracked from JIRA) |
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Included in CiviCRM version: N/A Core PRs: - civicrm#9875 - civicrm#10010
Instead of actually calling the delegated action in order to check if it's authorized or not, we now
use
Kernel->runAuthorize()
to do it.The way the check worked previously was: In order to be able to add an attachment to a entity X,
the user must have create permissions to X.
DynamicFKAuthorization
would then call X.createpassing the ID of the entity to which we want to add the attachment to. If the API call fails, it means
the authorization failed. Otherwise, the attachment creation would proceed.
The problem with this approach is that the API can fail for different reasons other than Authorization.
For example, a custom API can fail because it was expecting other mandatory fields (besides the id
given by
DynamicFKAuthorization
). By usingKernel->runAuthorize()
, we avoid this problem, since nowthe actual action will not be executed.
Since now we don't need to update the DB in order to check for Authorization, the transaction
isn't necessary anymore.