Monday, July 19, 2010

CRM Impersonation Explained

[LAST UPDATED: 2/28/2011  See Comments for details.]

When it comes to developing for CRM, I find it absolutely integral to understand how the code beneath it works.  However, since the source code for CRM is not freely available, certain behaviors are only discoverable by trail-and-error, or by documentation.  In this case, I want to illuminate the use of the term "Impersonation" with respect to CRM.

The Problem:

Impersonation as a word means:  “to assume the character or appearance of.”  I think that it is confusing to suggest that a user impersonates itself.  The SDK implies this with the following scenario:  that the Active Directory account associated to the “SYSTEM” user, which is executing the plug-in, impersonates the “SYSTEM” user (when no “impersonation” options are configured by plug-in registration).

So, what does the SDK mean?

The Situation:

The SDK defines Impersonation as follows:

“Impersonation is a technique that is used to execute business logic (code) on behalf of a Microsoft Dynamics CRM user to provide a desired feature or service using the appropriate role and object based security.”

This is simple enough, but then the article about Impersonation in Plug-ins muddies the water a bit:

“Plug-ins execute under the security account, which is specified on the Identity tab of the CRMAppPool Properties dialog box.”

“[If a specific user is not declared at registration], the calling/logged on user or the standard 'system' user is the impersonated user.”

“Impersonation in plug-ins is not supported while in offline mode.”

Consider the description in the SDK of how impersonation works:

“Impersonation involves two different user accounts. One user account (A) is used when executing code to perform some task on behalf of another user (B). To use impersonation, user account (A) under which the impersonation code is to run must be added to the PrivUserGroup group in Active Directory. This group is created by Microsoft Dynamics CRM during installation and setup. User account A does not have to be associated with a licensed Microsoft Dynamics CRM user. However, the user who is being impersonated (B) must be a licensed Microsoft Dynamics CRM user.” [emphasis mine]

While the language may lead you to believe that impersonation is an optional action for use with executing code on behalf of a user that is not currently executing code, the reality is that impersonation is unavoidable.  This leads me to struggle to understand the use of the word 'impersonation'.

What the documentation is implying, is a natural barrier between the Active Directory account that either executes the code, or is applied with ASP.Net impersonation (hereafter referred to as “Execution Account”), and the CRM User record tied to that web service request in the CrmAuthentiationTokenValue (hereafter referred to as “Caller”).  This distinction exists even between Active Directory accounts and CRM Users which are directly associated.

When the CallerId member of the CrmAuthenticationToken instance passed to the web service is empty, I believe CRM maps the Execution Account to a CRM User with an organization lookup using the Execution Account’s Active Directory identifier, which then becomes the Caller context within the operation.  I believe this mapping defaults to the “SYSTEM” CRM User if the Execution Account belongs to PrivUserGroup in Active Directory.  Regardless of the result, this is still impersonation.

As a convenience, when instantiating an ICrmService instance with false passed as a parameter to CreateCrmService(), the service object inherits the Caller mapping of the IIS Application Pool’s Execution Account via an internally stored CrmAuthenticationToken instance.  Additionally, the overloaded CreateCrmService() method allows you to pass true or a GUID value to change the CallerId to a value from the execution context.  These are still impersonation.

Regardless of what CRM User is defined for the Caller, the Execution Account will always be defined by the Active Directory account established in the Credentials member of the CrmService instance; and its credentials will always be implicitly used by the ICrmService instance.  This is the account that “executes” the code.  In this way, CRM’s impersonation mechanism inherits ASP.Net impersonation.

This leads me to consider using the terms “explicit impersonation” or “implicit impersonation” in conjunction with the statement: impersonation is always used; or, wholly define “impersonation” as any difference between the CRM User that is naturally mapped to the Execution Account and the Caller.

The later makes defining the use of the term “impersonation” crystal clear, but makes it difficult to determine the use of “impersonation” in the context of actual code, while the former is perhaps better for understanding the true mechanics of Active Directory impersonation of CRM Users in CRM, and closely matches the SDK’s definition of “impersonation”.  Therefore, as of today, I hereby personally adopt the former.

The Resolution:

An Active Directory account always impersonates a CRM User to perform CRM operations.  Even if the impersonated CRM User is directly associated with the Active Directory account.

An Execution Account must provide a Caller for CRM operations, either directly or indirectly.  Therefore, impersonation is unavoidable.  It holds true that CRM impersonation implicitly inherits ASP.Net impersonation, which does not explicitly declare a Caller unless specifically configured.  Ergo, impersonation can occur implicitly, or explicitly.

Explicit Impersonation:

Explicit impersonation is by far the most common use of impersonation, and involves instantiation of CrmService or ICrmService objects with specific GUID values for the CrmAuthenticationToken.CallerId.

The SDK documentation makes a careful distinction of how to instantiate a CrmService instance in a child plug-in.  The difference being, that in a plug-in, you have two user GUID references available for use, provided by the IPluginExecutionContext object:  InitiatingUserId, and UserId.  (Developers should be keen to notice that passing false to this function is not the same as passing false to IPluginExecutionContext.CreateCrmService().)  Because of its behavior, it can be said that this CreateCrmService() code always makes use of explicit impersonation.

The MSDN article “Authentication from an ASPX Page” shows how to use a special static method of the CrmAuthenticationToken class, called ExtractCrmAuthenticationToken().  With it, a custom page embedded within CRM from the ISV-folder hierarchy can retrieve the current instance of the CrmAuthenticationToken in use by the IIS session for the Execution Account.  It’s imperative to establish the Caller apart from the Execution Account, because in order to establish a connection to IIS from IIS, the Execution Account requires reversion from the thread identity to the process identity through the use of CrmImpersonator.

Implicit Impersonation:

Implicit impersonation is always used by offline-mode plug-in execution, instantiation of ICrmService with a false parameter passed to IPluginExecutionContext.CreateCrmService(), or instantiation of CrmService which is passed a CrmAuthenticationToken object without a value for its CallerId member.  This form of impersonation will always force CRM to map the Execution Account to a Caller; which can be “SYSTEM” for Execution Accounts in the PrivUserGroup, or an appropriate CRM User account within the targeted Organization.

The Result:

While I am certain that, most developers do not have a problem with using impersonation or deciding which user references to supply to their web-service calls, I think it is important that we use proper terminology to define the actual behavior of the system.  Overall, the way we use the spoken language to define a process conveys particular assumptions about the process.  Whenever disparity exists between the spoken and computer languages defining a process, confusion can abound.

I realize, fully, that I have invented this problem.  Even if “impersonation” is only supposed to describe the difference between an Execution Account’s naturally mapped CRM user and the Caller, the documentation is not congruent with that paradigm.  I think the distinction between the Active Directory account and a CRM User record props a natural case for defining “impersonation” as the Active Directory account taking on the form of the CRM User record.

Nonetheless, I hope to improve the terminology used to define the application interaction do reduce my own personal confusion, and any unspoken confusion held by developers who struggle to understand CRM better.