Pages

Thursday, July 29, 2010

Default Entity for Multi-Entity Lookups

[UPDATE (11/19/2010): I found an example of the method explained in this post at Stunnware’s blog.  I’m a big fan of Michael Höhne and his work, and therefore pay my respects to him for having written about this long before I did.  Perhaps I had even seen it before, but then forgot.]

After hacking the death out of CRM's internal scripting and behavior files, for form controls, I've come to understand some of the innate, undocumented, and probably highly unsupported features that the controls have.  In a recent CRM Developer thread, I found my experience in this area highly valuable.

Lookup fields are pernicious beasts when it comes to customization.  Their tie-in with the Lookup dialog makes them all the more difficult to control.  So, wherever Microsoft has implemented a function that is easy to reuse, I encourage it--often with clear declaration that Microsoft doesn't support such undocumented use.  I'd like to cover more of what I've discovered in an alternate article, but I thought that today I'd share a tidbit:  making Lookup fields that target multiple record types, or entities, default to a particular entity.

The Lookup field exposes a member called defaulttype in the DOM, and its value is set as the Object Type Code representing the Entity which will be selected as default for the Lookup dialog.  For instance, the “To” field on an Email record supports four Entities: Account, Contact, Lead, and User.  The standard default entity, displayed in the “Look For” selection of the Lookup dialog, is the Account (OTC: 1).  If I want to instead set the default as the Contact (OTC: 2), I would use this code in the Email’s OnLoad event:

crmForm.all.to.setAttribute("defaulttype", "2");

Thursday, July 22, 2010

Update to the Javascript Grid Editor documentation.

The "Advanced Scenarios" section of the Javascript Grid Editor's documentation has been updated with a new write-up on how to dynamically specify configuration parameters for related views.  This will hopefully address the issue of how to use the JGE in associated or embedded views, effectively.

This isn't a code update, as I'm still working on the next update that will give the JGE the ability to work with Money fields, and special "child entities" like the Quote/Order/Invoice Product.

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.

Thursday, July 15, 2010

Update to the Javascript Grid Editor: 1.0.10 (Now with improved initialization!)

There come a time when every developer finds new, improved ways of doing things.  I'm no exception to this, and when I learn something new, I try to implement it wherever I may find it beneficial.

Today's study is about the use of Javascript to dynamically load and initialize script and CSS from external files:

Originally, the Javascript Grid Editor used an asynchronous method of achieving this for both external Javascript and CSS files.  While this worked, it was terribly slow, as each script file was loaded and processed by IE asynchronously, each reporting back to a shared handler that check the status of all outstanding resources.  Crude, but effective.  What it was not, however, was efficient.

Because of the way I was hacking together CRM controls for the JGE, I required these external sources to load before proceeding to implement the controls.  Most users of the JGE experienced this requirement by way of a load time that was either very long, or didn't work at all!  That said, I had been on the lookout for a better method of synchronously loading these scripts without using the dreaded eval() statement.

Today is the day I found it.  Thanks to a very thorough posting by Stoyan Stefanov, I was able to make both the external Javascript and external CSS files load in a synchronous manner, thereby improving the responsiveness and initialization times of the JGE incredibly.  This marks the resolution of a particularly troubling bug I've dealt with in my own deployment.

If you found that the JGE hadn't worked properly for you in the past, I encourage you to grab the latest release.

[ADDENDUM: I've also included the altered code in the IncludeScript(), IncludeStylesheet(), IncludeExternalScript(), and IncludeExternalStylesheet() functions in the CRM Javascript Library.]

Thursday, July 1, 2010

I'm a Microsoft MVP?!

If you had asked me a year ago if I thought I qualified for MVP material, I would have flatly said, "No."  It's with a thankful heart then, that I'm glad nobody asked me.  Today, I received the much anticipated declaration that I have, in fact, been awarded the Microsoft 2010 MVP.

I've been a regular to the CRM Development Forum since I was first introduced to CRM 3.0.  At first, I was simply an avid reader, following the tribulations of others and learning what I could from the masters (like David Jennaway, Michael Hoehne, Adi Katz, Daniel Cai, Andriy Butenko, and so many more).  About 2 years ago, I really started to get involved with answering questions, and seeing if I could keep my technical edge sharp even if I hadn't personally faced a situation being presented.

I'd like to issue a special "thank you" to Andriy Butenko.  About a year ago, during this MVP period, we got into a silent battle with each other over holding the "Top Answerer" spot in the forum.  Without this undeclared competition, I may not have had the sheer quantity of involvement necessary to qualify for an MVP award.  Perhaps he never knew the battle ever existed, or what I had determined to do.  However, we both certainly excelled at answering questions during that period, and I'm happy to say that I succeeded at holding the top spot for about a month.

It's my hope that the coming year will see as much involvement from me as the previous year.  I wholeheartedly thank and appreciate the CRM Community and Microsoft for this recognition.  I will live up to its status and meaning as much as I possibly can.