Pages

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.

10 comments:

  1. This article has been edited a few times since the original posting, either for clarification, structure, formatting, or technical accuracy. I openly welcome refutations of any incorrect statements I have made.

    ReplyDelete
  2. Ok, I think I've just posted my last edit to the article, and like the way it stands at this moment. Unless I receive any constructive criticisms from readers, it will not likely change after this point.

    ReplyDelete
  3. Added additional information about the relationship between ASP.Net credentials and a CRM User. I think the article now elaborates the conclusion in context of additional text from the SDK. Whew! I'm about done with this forever at this point.

    ReplyDelete
  4. I found a delightfully informative write-up on .NET impersonation here: http://alt.pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsImpersonation.html

    It really shed some light on my understanding of impersonation, and tends to reinforce my ideas regarding the distinction between a CRM User construct and user identity, and the nature of impersonation.

    ReplyDelete
  5. Fixed various areas, added more supporting content to "Explicit Impersonation". Tried to improve readability and clear up terms that were confusing.

    ReplyDelete
  6. I think I might commit to reading this through about once a week, to convince myself that I at least begin to understand what's going on.

    I've built maybe... a dozen or maybe more ISV apps in the last 5-6 months of working with CRM, but it seems every time I deploy one, I encounter a new issue; predominantly authentication related.

    ReplyDelete
  7. Hi,
    We are tied up with a very peculiar issue. I landed up here....
    ==============
    We had the CRM run on iis6 under 2003. Under the IIS there was a custom asmx webservice which accessed the CRM DB for fetching specific data. This app did not have its own web.config too.

    This webservice was consumed without any issues until it was moved to iis7, on 2008R2.
    NOw here is the issue:

    ================
    The iis has a web application , which holds the asmx webservice. Using localhost, when invoked inside the server , the methods returns the data ( return is a dataset from CRM4.0 DB).
    The same when accesses remotely by another website app hosted on a different server , it gives away empty dataset.
    From this website ,normal 2007 crm service is working fine. The same credentails and token are use by this app also, still , Dataset is empty...
    This app has 2 check methods which returns a hardcoded string as well as a hardcoded dataset. BOth works fine. While SQL -Server Data is not getting populated outside !!!

    We tried checking the IIS7 setings.. but no help ! .. tried with SQL server access .. still no clue !..
    By the behavior outlined above, do you have any points to share?

    ReplyDelete
  8. Sanu,

    It's hard for me to say, exactly, what the issue is. But I would recommend taking things like this to the CRM Development forum for further assistance. My first thought would be to understand what user is being passed into the CallerId value of the CrmAuthenticationToken instance that is issued by the "different server".
    I would imagine that the server would throw a privilege error if you were not providing an authenticated CRM user; so therefore I must assume that the connection coming from the "different server" is using credentials that belong to the PrivUserGroup in Active Directory or to an actual established user account in CRM. Therefore, I suspect the empty dataset could be due to the security roles applied to the records sought by the authenticated/impersonated account.
    Again, if you don't find this answer sufficient, I encourage you to bring the discussion to the CRM Development forum.

    ReplyDelete
  9. Clarified the paragraph around the use of the "false" value in conjunction with the function CreateCrmService() as a matter of convenience.

    ReplyDelete
  10. Thank you very much for a well explained and detailed explanation regarding CRM Authentication and Impersonation.

    This would be very helpful for those who are new in CRM.

    In my experience, when we usually talk about impersonation, we usually talk about Impersonating another User with a "higher" privilege/access on the entities/records that the 'calling' user does not have access or privilege :)

    For example, a regular CRM user creating an Account record, and a plug-in runs to fetch data and update newest running number to an entity, where the regular CRM user does not even have a Read privilege. So in this scenario, a user with a 'higher' privilege is impersonated :)

    Thanks for a Technical explanation! :)

    ReplyDelete

Unrelated comments to posts may be summarily disposed at the author's discretion.