Skip to content
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

Add checkAccess API & BAO functions + hook_civicrm_checkAccess #20170

Closed
wants to merge 7 commits into from

Conversation

colemanw
Copy link
Member

@colemanw colemanw commented Apr 27, 2021

Overview

This combines work done by @eileenmcnaughton in #20035 with WIP from #19837 to make a checkAccess function that works at both the APIv4 and BAO layers. It standardizes on api-style entity & action names, and provides hooks the opportunity to escalate permissions or deny access on a per-record basis.

image

@civibot
Copy link

civibot bot commented Apr 27, 2021

(Standard links)

@eileenmcnaughton
Copy link
Contributor

Yeah I am thinking about my previous approach - I guess that although we can do WHERE on the update & delete we actually call each one individually. As long as we are not having to decide about too many edit links at once then checking 1 by one is OK

CRM/Core/DAO.php Outdated
* @param array $record
* Array of all known values for the record
* @param null $contactID
* User id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be clarified as the contact is whose permissions are being checked (otherwise on some api it could be the contact who we want to know about permission to)

@eileenmcnaughton
Copy link
Contributor

This all looks good & I think we have test cover in the changes to the financialacls code

I kinda wonder about performance - an extra hook call every create/update but I guess we already do the pre & post hook & it has caching but @totten do you have any thoughts on whether that should be a consideration?

I'll give it a final r-run if we confirm this & you also fix the one minor code comment above

@colemanw colemanw marked this pull request as draft April 28, 2021 23:45
@totten
Copy link
Member

totten commented Apr 29, 2021

On the performance front... obviously, it's hard to say authoritative things without usage-profiles/benchmarks. My gut says:

  • Agree it's ultimately useful to be able to extend access checks.
  • Yes, it's OK to fire a hook during a read - if you're expecting 0-4 listeners, then the extra dispatch work isn't much.
  • But, I'm skeptical about having the API issue sub-requests via AbstractAction::checkAccess() => civicrm_api4($entity,'checkAccess'). The problem is that API sub-requests are also deeply hookable, so it may fire another half-dozen events with 50 listeners. And basically all of them will be doing interest-checks to say "No, I don't have any interest in changing this API call".

It does seem that this arrangement allows you to have one implementation of checkAccess which is shared for both internal-consumers ($this->checkAccess()) as well as external-consumers (POST /civicrm/ajax/api4/Contact/checkAccess). That seems good. However, I think you could get this same consistency/sharing with less overhead -- e.g.

  • Civi\Api4\Utils\CoreUtil::checkAccess() has your main access check (basically, moving the current CheckAccessAction::_run())
  • Internal calls to $this->checkAccess()delegate to CoreUtil::checkAccess() (or are straight-up replaced by CoreUtil::checkAccess()).
  • The public-facing CheckAccessAction::_run() delegates to CoreUtil::checkAccess().

In theory, this loses the ability to customize $this->checkAccess() for each entity. But I think maybe that's a good thing. There are currently 3 ways to customize checkAccess (impl hook_checkAccess, impl $baoName::checkAccess(), and impl MyAction::checkAccess()). Even if you drop MyAction::checkAccess() as a customization-point, you still have 2 more.

@colemanw
Copy link
Member Author

colemanw commented Apr 29, 2021

That's a good suggestion @totten - while we're at it, the CoreUtil::checkAccess() could be the central place to dispatch your new civi.api4.validate event (which we might consider renaming to civi.api4.checkAccess).

@colemanw colemanw force-pushed the acl branch 2 times, most recently from 264bd04 to 9a6f44a Compare May 6, 2021 17:59
@seamuslee001
Copy link
Contributor

@colemanw I think test failures relate here

@colemanw colemanw force-pushed the acl branch 2 times, most recently from 3749a8e to 8c39d4a Compare May 8, 2021 00:20
@colemanw colemanw marked this pull request as ready for review May 8, 2021 02:52
@colemanw
Copy link
Member Author

colemanw commented May 8, 2021

This is now passing tests and also increases coverage by removing the early return in ACLPermissionTest.php

@eileenmcnaughton
Copy link
Contributor

@jackrabbithanna FYI this might be useful to you once merged. The immediate use-case is to determine whether to permit edit-in-place on search results

@jackrabbithanna
Copy link
Contributor

jackrabbithanna commented May 18, 2021

@eileenmcnaughton I've been watching this. Definitely thinking about its application for access control discussion https://github.com/eileenmcnaughton/civicrm_entity/issues/296

@totten
Copy link
Member

totten commented May 18, 2021

@colemanw, a few things:

  • Added a commit to normalize/improve some of the docblocks.

  • Rebased to cleanup some commit messages ("getAccess"=>"checkAccess")

  • We probably need some kind of test-coverage here. I've pushed a revision for APIv4's ConformanceTest which ensures that hook_checkAccess runs during some actions (and that &$granted is respected). On my local, this seemed to pass with most entities - by EntityTag and UFJoin failed. Here's the failure for EntityTag:

    TypeError: Argument 5 passed to CRM_Utils_Hook::checkAccess() must be of the type boolean, null given, called in /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/CRM/Core/DAO.php on line 3075
    
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/CRM/Utils/Hook.php:2103
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/CRM/Core/DAO.php:3075
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Utils/CoreUtil.php:185
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Generic/AbstractCreateAction.php:70
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Generic/DAOCreateAction.php:51
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Generic/DAOCreateAction.php:36
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Provider/ActionObjectProvider.php:68
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/API/Kernel.php:149
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/Civi/Api4/Generic/AbstractAction.php:239
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/tests/phpunit/api/v4/Entity/ConformanceTest.php:269
    /home/totten/bknix/build/dmaster/web/sites/all/modules/civicrm/tests/phpunit/api/v4/Entity/ConformanceTest.php:152
    /home/totten/bknix/extern/phpunit6/phpunit6.phar:570
    

    It seems the handling of $granted changed a bit mid-patch? Most of the time, $granted is passed-by-reference, but for EntityTag (CRM_Core_DynamicFKAccessTrait) there's a case where it's @return bool. This fails b/c it returns a NULL. Maybe the signature should be lined-up better as part of a fix?

  • I wasn't completely sure which actions are supposed to support hook_checkAccess, but (based on skimming CRM_Contact_BAO_Contact::_checkAccess()) it seems that at least 3 would be expected: create, get, delete. I also drafted a commit which tests it for get (9685ce6). However, I didn't push this up because it seems to fail across the board.

@totten
Copy link
Member

totten commented May 20, 2021

Trying to get my head around the code, I made some notes which may be useful for anyone reading this:

@eileenmcnaughton
Copy link
Contributor

@colemanw @totten so status is that the unit test fails need addressing & this is more or less agreed?

I have come across a couple of things that might be able to leverage this but might require more thought

  1. register for event - there are permission checks in the event registration forms to see if someone can register for an event & if they can register for the specific event. This is not an api action per se but it is in the larger space

  2. CustomField::check() - I've been looking at this PR Respect ACL for multi-value custom field (originally PR#15347) #19026 & wondering if it could leverage the new api

@colemanw colemanw force-pushed the acl branch 2 times, most recently from 6545797 to b2ee53f Compare May 26, 2021 02:57
@colemanw
Copy link
Member Author

I think I fixed the test.

@eileenmcnaughton
Copy link
Contributor

nope

@colemanw
Copy link
Member Author

Jenkins couldn't apply the patch. I squashed a couple commits let's see if that helps.

@totten
Copy link
Member

totten commented Jun 3, 2021

jenkins, test this please

eileenmcnaughton and others added 7 commits June 2, 2021 23:30
This adds a static ::checkAccess function to all BAOs, which dispatches to
a protected _checkAccess function in that BAO, as well as a new hook:
hook_civicrm_checkAccess($entity, $action, $record, $contactID, &$granted)
Call checkAccess action before creating, updating or deleting
…hone, etc.)

Implements the _checkAccess BAO callback for contacts and the related entities
listed in _civicrm_api3_check_edit_permissions.

Switch APIv4 to stop using _civicrm_api3_check_edit_permissions
now that the checks are implemented in the BAO.

Also fixes a couple permission check functions to respect $userID variable.
…check access

The UFJoin test was failing because Get was returning no results due to an empty entity_table field.
The field is optional so ACLs shouldn't be based on it.

Updates conformanceTests to ensure hook permissions apply
@totten
Copy link
Member

totten commented Jun 10, 2021

Replaced by #20533

@totten totten closed this Jun 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants