How to Get the Impersonated User Id in a Plugin?
Asked Answered
T

3

6

Within the Plugin Context, there are two user ids,

  1. InitiatingUserId which returns the id of the user who actually fired the plugin.
  2. UserId which returns the user Id of the user the plugin is actually running under. (this is the user specified when registering the plugin, or the Calling User, if it's registered to run as the calling user)

But I'm interested in a third user id, the impersonated user id of the OrganizationServiceProxy that performed the call.

Lets say I have a ASP.Net website that is running with a CRM System Admin AD account, and any calls that are made to CRM, use impersonation, looking up the CRM userid of the person currently logged into the site.

This works great for selects and updates, the user is only able to see\update what they have rights to. But... if I create a plugin that is is triggered on the update of a particular entity, how do I lookup the user id of the impersonated user on the Asp website from within the plugin?

Thyratron answered 23/5, 2013 at 20:38 Comment(9)
I read your question 4 times, if I understood you have this scenario: user DOMAIN\SMITH is logged inside a site running under DOMAIN\IAMSYSADMIN (application pool), and inside a triggered plugin running with calling user DOMAIN\PLUGINUSER, you want to know that was DOMAIN\SMITH and not DOMAIN\SPECTER the initial user, right?Excruciate
This information should be available in InitiatingUserId. Do you get any other value? There is always WhoAmI() request that might help in your case.Allegedly
@Allegedly The InitiatingUserId in my example will be the id of the application pool user, not the impersonated user. The WhoAmI request will return the Id of the User running the plugin.Thyratron
@GuidoPreite If by DOMAIN\SPECTOR you mean DOMAIN\IAMSYSADMIN, then yes. I believe you understand it correctly.Thyratron
what I meant is that you want to know the user logged in the site (DOMAIN\SMITH or DOMAIN\SPECTER (another user of the website), because you already know that the application pool is DOMAIN\IAMSYSADMIN and is what you got from InitiatingUserId, you want to go deeper one level more.Excruciate
@GuidoPreite You're correct sir.Thyratron
@Thyratron add some tags and offered a bounty, hope it helps, I'm curious too!Excruciate
So to confirm, user A is accessing the site which is running under user B. All calls made to CRM are through user B, but set to impersonate user A? And what you're looking to find is user A in the plugin?Domingodominguez
@Domingodominguez Yes, you're possibly missing that the plugin is registered to under User C. as well.Thyratron
D
3

As far as my understanding of the situation goes, you are able to retrieve the User Id of the impersonated user, but not the authenticated user. Below are some scenarios mapped out that should hopefully clarify the behavior (tested in UR11 and UR12):

  1. Authenticated to CRM as User A, impersonating User B. Plugin is not registered to impersonate any specific user (i.e. ImpersonatingUserId of plugin step is set to null (from the plugin registration tool, "Run in User's Context" would be set to "Calling User)). Example would be with OrganizationServiceProxy authenticated as User A, where proxyService.CallerId is User B.
    • PluginContext.UserId = User B
    • PluginContext.InitiatingUserId = User B
    • createdonbehalfby (or modifiedonbehalfby) = User A
  2. Authenticated to CRM as User A, impersonating User B. Plugin is registered to impersonate User C. (i.e. ImpersonatingUserId of plugin step is set to User C (from the plugin registration tool, "Run in User's Context" would be set to User C)). Example would be with OrganizationServiceProxy authenticated as User A, where proxyService.CallerId is User B.
    • PluginContext.UserId = User C
    • PluginContext.InitiatingUserId = User B
    • createdonbehalfby (or modifiedonbehalfby) = User A

Scenario #1 is a bit confusing to me as I vaguely remember that working differently. This must mean that when the CallerId property is set, the impersonation happens in a different location than with Scenario #2. However, you can see that in Scenario #2, we seem to achieve your expected result.

The steps I used to produce this result were:

  1. Create Console Application and reference Microsoft.Xrm.Sdk and Microsoft.Crm.Sdk.Proxy. Instantiate a new OrganizationServiceProxy object where the authenticated user is User A. Set the CallerId to be the Guid representing User B.
  2. Create a super simple plugin that just throws an exception the first chance it gets like this:

    throw new Exception(string.Format("Initiating Id: {0}, User Id: {1}", 
        context.InitiatingUserId, 
        context.UserId));
    
Domingodominguez answered 27/5, 2013 at 17:7 Comment(11)
Is this for CRM 4.0? I'm not finding any ImpersonatingUserId for CRM 2011. And I'm having trouble recreating your scenario #1.Thyratron
@Thyratron This is for 2011. Let me try to whip up a sample this morning and I'll update the question. Where are you looking for ImpersonatingUserId and not finding it? Plugin registration or org service setup?Domingodominguez
@Thyratron I made an update to clarify what ImpersonatingUserId equates to in the plugin registration tool. Let me know if that doesn't clarify things for you.Domingodominguez
Thanks, that clarified it. I swore I attempted Scenario 1 and both the UserId and InitiaitingUserId in the plugin context was UserA. Can you verify your steps?Thyratron
@Thyratron I could've sworn I saw something different. I have updated the answer with what I am seeing in an actual test of UR11 and UR12.Domingodominguez
I was surprised as well when I was testing scenario #1. I expected to see UserB as the UserId. I know that the IOrganizationService in the plugin has to be aware of UserB somehow because (and you can correct me if I'm wrong) the plugin will run with only the rights assigned to UserB. I'll have to spend some time to determine if there is a way to use reflection to look that up.Thyratron
@Thyratron I apologize, I had a typo. I updated the answer ... in scenario #1 I was seeing only User B, not User A at all.Domingodominguez
@Thyratron Just added some more information about how to get the other user id for Scenario #1. Thanks to the friends at the Microsoft forums: social.microsoft.com/Forums/en-US/crmdevelopment/thread/…Domingodominguez
@Daryl, if you can confirm that is a working solution I will give the bounty to GotDibbs, unfortunately I have no time to test the suggested solutionsExcruciate
I think this is close, the only problem is, my plugin is running in a prevalidation step, so I'm not sure if there are created or modified on behalf of values in the entity that will be updated.Thyratron
@Thyratron I just took a look, User A from Scenario #2 is not available in prevalidate anywhere in the plugin context. I'm a bit confused though, aren't you actually looking for Scenario #2's User B?Domingodominguez
T
0

Just wanted to clarify that GotDibb's data is correct, even though my question contradicts it.

I created a UserIdTest Plugin as follows

public void Execute(IServiceProvider serviceProvider)
{
    var context = serviceProvider.GetContext();
    var service = serviceProvider.GetService(context);
    var iUser = service.Retrieve("systemuser", context.InitiatingUserId, new ColumnSet("fullname"));
    var uUser = service.Retrieve("systemuser", context.UserId, new ColumnSet("fullname"));
    throw new Exception(string.Format("Initiating: {0}, User: {1}", 
        iUser.GetAttributeValue<string>("fullname"),
        uUser.GetAttributeValue<string>("fullname")));
}

I Performed the same testing as GotDibbs, and got the same answers, which confused me because I wouldn't have asked the question if I wasn't somehow getting a different answer. But then I realized that the issue I was seeing was being caused by a recursive plugin.

The first call to the plugin worked as expected, but when the plugin triggered another call to the plugin, it used the plugin's Context user's credentials (which makes sense) and lost the impersonated user id's credentials. Here is a table to hopefully help clarify what happens

First for the initial plugin call:

+--------------------+------------ +----------------------+----------------------------------------+
|    Org Service     | Org Service |    Plugin Step Run   |               Results                  |
| Client Credentials |  CallerId   |   in User's Context  |                                        |
+--------------------+------------ +----------------------+----------------------------------------+
|                    |             |                      | InitiaitingUser : ServiceAccount       |
| ServiceAccount     | None        | PluginServiceAccount |                                        |
|                    |             |                      | UserId : PluginServiceAccount          |
+--------------------+------------ +----------------------+----------------------------------------+
|                    |             |                      | InitiaitingUser : UserBob              |
| ServiceAccount     | UserBob     | PluginServiceAccount |                                        |
|                    |             |                      | UserId : PluginServiceAccount          |
+--------------------+------------ +----------------------+----------------------------------------+

And second for the Plugin Depth > 1

+--------------------+------------ +----------------------+----------------------------------------+
|    Org Service     | Org Service |    Plugin Step Run   |               Results                  |
| Client Credentials |  CallerId   |   in User's Context  |                                        |
+--------------------+-------------+----------------------+----------------------------------------+
|                    |             |                      | InitiaitingUser : PluginServiceAccount |
| ServiceAccount     | None        | PluginServiceAccount |                                        |
|                    |             |                      | UserId : PluginServiceAccount          |
+--------------------+-------------+----------------------+----------------------------------------+
|                    |             |                      | InitiaitingUser : PluginServiceAccount |
| ServiceAccount     | UserBob     | PluginServiceAccount |                                        |
|                    |             |                      | UserId : PluginServiceAccount          |
+--------------------+-------------+----------------------+----------------------------------------+
Thyratron answered 12/6, 2013 at 17:3 Comment(0)
A
-1

This same issue is also relevant in CRM 2013 and probably has the same solution:

Taking the scenario where a Plugin is registered to execute under the context of the calling user, the plugin execution context's InitiatingUserId and UserId values would both be the same.

This is true even if the client established the connection by overriding the CallerId - in this case both values would represent the CallerId value specified instead of the identity of the user that was authenticated.

So how to get the actual caller's identity inside the plugin? Post phase is easy, just check createdonbehalfby or modifiedonbehalfby in a post execution image. Pre-Operation is not as obvious as these values don't exist anywhere but the trick is simple.

Inside the plugin, establish a new IOrganizationService where the UserId is NULL, not context.UserId or context.InitiatingUserId but instead just null. Then using that service, execute a WhoAmI request and the response will contain the GUID of the authenticated user that executed the service call instead of the identity of the CallerId that was specified.

It turns out that the IOrganizationServiceFactory actually has a private field that contains this value (AdministratorId) but since it is private it isn't available to read so instead this workaround will work at the cost of an extra overhead.

Artur answered 20/1, 2015 at 0:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.