-
-
Notifications
You must be signed in to change notification settings - Fork 825
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 ability to temporarily override logged in user #15151
Conversation
(Standard links)
|
c44956b
to
ecce8b9
Compare
7d3ce29
to
bcc002c
Compare
@michaelmcandrew this might be of interest to you, @colemanw guessing this could be used especially for re-calculating recipients for a mailing right? |
thanks @seamuslee001! I've made a note (see reference above). |
also paging @pfigel in case you have any thoughts on this |
Makes sense to me. 👍 |
Pasing 0 for $contactId or a contact without a UF record will now correctly check against anonymous users permissios.
Allows api4 to specify a different acting user
// first see if the contact has edit / view all permission | ||
if (CRM_Core_Permission::check('edit all contacts', $contactID) || | ||
($type == self::VIEW && CRM_Core_Permission::check('view all contacts', $contactID)) | ||
) { | ||
return $deleteClause; | ||
} | ||
|
||
if (is_null($contactID)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this change basically permits 0 to be passed in - why is it moved below the other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eileenmcnaughton the problem was that this function wasn't differentiating NULL
from 0
and they are very different.
0
= no user
null
= current user
I fixed that and then moved the block down because CRM_Core_Permission::check
already handles NULL
and 0
correctly so there was no need to mess with the $contactID
value before calling that function.
ping @christianwach can you look at this as well? |
return FALSE; | ||
} | ||
$handle = $handle ?? uniqid(); | ||
self::$userOverride[$handle] = $contactId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
Hmm, if there's going to be a static listing, then probably use
Civi::$statics
? -
It seems like this query ought to be invoked any time there's a transition of the active user ID, regardless of who/how/why it's transitioning.
-
Maybe this function should fire some kind of "set user" event? It feels a little brittle for any one party to enumerate all the things that happen when the user changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, part of me thinks that the whole of CRM_Core_Config::singleton()
and Civi::$statics
should be rebuilt/swapped whenever one switches the active user (to categorically prevent leaking data via caches). On the flip side, that's probably slower (esp if there are many user changes within the PHP request-lifespan) and rather complicated, and those leak bugs are hypothetical. It's equally plausible from my POV that cache-leak vulnerabilities turn into long-term whackamole... or never happen at all...
However, I'm very much on the fence about that - just want to verbalize to see what others think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only one I've looked at is the contact ACL cache, which is on a per-user basis so it works fine with these overrides. But you're right there may be other spots in the system where things are cached assuming the logged in user id won't change.
My original use case for this is pretty limited though. I wanted an anonymous user, authenticated with a checksum, to be able to submit an afform as that contact. So the afform.submit call internally authenticates the checksum and then sets "acting_user" param on subsequent api calls. So that's not nearly as demanding a scenario as actually "masquerading" as a different user and clicking around the site.
|
||
public function testOverride() { | ||
$session = \CRM_Core_Session::singleton(); | ||
$originalUser = $session::getLoggedInContactID(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little surprised PHP 7 doesn't complain about dispatching a static-function on an object. Learn something new...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really different from doing self::fooParentFunction()
from within the object.
Thanks for the heads-up @kcristiano. I'm not sure I understand the need for this given that we already have |
@christianwach so this is about actually having the ACL rules applied as if your another user not the current user either an annon or another logged in user |
Part 1, GeneralI confess to vacillating a lot while digesting this, and it seems to me that there's a continuum.
The two ends of the continuum feel like a trade-off between cost/feasibility and quality/bugs:
I think it might be good to use a phone-call to:
Part 2, An ExperimentFor the session things... I played around with generalizing the "userID override" and making a "subsession", wherein:
That experiment has some bits that I liked+disliked:
|
Ok so in terms of how to proceed - today's decision is 'do we give up on getting this into the rc & target 5.19 instead. The above reads like a 'yes'. However I would note that mitigating the risk / in argument of merging this my take on it is that this would be non-regressive & the only risks are for people attempting to use a new feature - in which case it could be merged & if necessary iterated on Either way we should merge or punt & get 5.18 cut once the current MOP PRs have passed tests |
I'm fine with punting this to the next release while we discuss. |
Betty and I are reviewing PR's and we came across this one. As there is no reaction on @eileenmcnaughton requests of whether this pr needs to be merged. We think it is wise to close this one. |
re-closing as not currently on the go |
The main impetus behind this was to allow hyperlinks to Afform with an authentication token. Coleman, Seamus, and I talked this through a bit further and realized the same goal could be addressed by changing the way the authentication token works. For this, the spiritual successor is #19590. There could be other cases where one needs to override the user temporarily, but these should be rare. Here's a sketch of how one might achieve that once the other PR is done (details subject to change): // Make an authentication token to elevate privileges
$token = Civi::service('crypto.jwt')->encode([
'exp' => time() + 60, // Token is only valid for the moment we need it
'civi.cid' => 1234, // The user we want to run as
]);
// Send a sub-request with this authentication token
$handler = HandlerStack::create();
$handler->unshift(\CRM_Utils_GuzzleMiddleware::url(), 'civi_url');
$http = new \GuzzleHttp\Client(['handler' => $handler]);
$http->post('civicrm/ajax/rest', [
'headers' => ['Authorization' => "Bearer $token"],
'form_params' => ...,
]); |
Overview
This adds a function in CRM_Core_Session to temporarily masquerade as a different user, which api4 can use to specify the acting user for certain api calls.
Before
Current user taken from session, permissions always checked against current user.
After
Current user can be overridden, permission checks respect override, e.g.
If the given
$contactId
does not have a corresponding user account (with permissions), then permissions will match the anonymous user.Comments
Added a simple E2E test for the overrides.
This patch does not directly change the value of
userID
in theCRM_Core_Session
- it does not intend to persistently change the active the user. Rather, this provides a momentary escalation. To accomplish this, it:CRM_Core_Session::getOverriddenUser()
) which may be consulted on a bespoke basis to determine if there is an active override, andget('userID')
so to respect this value.