Monday 24 June 2013

CRM 2011: Using FetchXML with JavaScript to check for a user privilege client side

I recently had the need to perform a client side check if a user had Create privilege on the Account entity.  I knew it should be simple to create a FetchXML query to find this information, so all I needed was to execute this FetchXML query from the form.   There are a number of handy JavaScript libraries to to execute FetchXML and I decided to go with the XrmSvcToolkit.

So here is my new FetchXML privilege check function using the XrmSvcToolkit.

function HasUserPrivilage(UserId, PrivilageName) {
    var hasPrivilege = false;
    var fetchXml = "<fetch mapping='logical' version='1.0'>"
            + "<entity name='privilege'>"
            + "<attribute name='accessright' />"
            + "<attribute name='name' />"
            + "<filter>"
            + "<condition attribute='name' operator='eq' value='" + PrivilageName + "' />"
            + "</filter>"
            + "<link-entity name='roleprivileges' from='privilegeid' to='privilegeid'>"
            + "<link-entity name='role' from='parentrootroleid' to='roleid'>"
            + "<link-entity name='systemuserroles' from='roleid' to='roleid'>"
            + "<filter>"
            + "<condition attribute='systemuserid' operator='eq' value='" + UserId + "' />"
            + "</filter>"
            + "</link-entity>"
            + "</link-entity>"
            + "</link-entity>"
            + "</entity>"
            + "</fetch>";

    XrmSvcToolkit.fetch({
        fetchXml: fetchXml,
        async: false,
        successCallback: function (result) {
            if (result.entities.length > 0) {
                hasPrivilege = true;
            } else {
                hasPrivilege = false;
            }
        },
        errorCallback: function (error) {
            throw error;
        }
    });
    return hasPrivilege;
}

And usage

HasUserPrivilage(Xrm.Page.context.getUserId(), "prvCreateAccount");

Wednesday 5 June 2013

CRM 2011 - Using URL Rewrite (redirect) to make access to Internal and External URL seamless for end user

Problem

When you configure IFD (Internet Facing Deployment) for Dynamics CRM 2011 you must specify a different URL for internal and external access.  For an end user it is just confusing that they have to use a different URL when they are in to office vs when they ore out of the office.

Technically there is a good reason for the 2 URLs.  They are mapped to different Relying Party Trusts in ADFS.  The Relying Party Trusts for the Internal URL contains a claim which allows for Windows Authentication which means the user does not have to enter their username and password when accessing the site.  The Relying Party Trusts for the External URL does not have this claim and presents the user with an ADFS log in page.

In this post I will use URL Rewrite 2.0 module for IIS to automatically redirect the URLs so that external users will be redirected to the External URL and internal users will be redirected to the Internal URL no matter which URL they use.  This means you need only tell your users of about one URL (I suggest the external one) and it should avoid confusing the end user.

Some issues caused by the 2 URLs 
  1. The end user needs to know both URL's
  2. On the internal URL, if the user "Copy's a Link" and emails it to a colleague, the link will only work while the colleague is on the internal network
  3. Workflows which create emails with direct links to opportunities/cases/etc should they contain the internal or external URL or both?

Brian DeMarzo on sides of march has a very good article on redirecting the external URL to the in internal URL if the user is on the internal network. Brian's article addresses Problem 1 above but does not address Problem 2 or Problem 3.

I would like to extend Brian's to address Problem 2 and Problem 3 by redirecting requests to the Internal URL which are made from outside the network to the External URL.

My Environment

  • For simplification my environment will consist of 1 CRM server (but this idea could be applied to a CRM farm).
  • My CRM Internal URL is icrm.mycompany.com
  • My organisation name is OrgX (hence my external URL is OrgX.mycompany.com)

Solution

  • Install URL Rewrite 2.0 on the CRM Server.
  • Create a second ip address on your CRM server.  We will use these ip addresses to determine if the traffic is coming from inside or outside my network.
  • Create internal DNS records for icrm.mycompany.com  and OrgX.mycompany.com to resolve to IP Address 1 (in my case 10.1.1.1)
  • On you firewall/routing server map all requests for icrm.mycompany.com and OrgX.mycompany.com to IP Address 2 (in my case 10.1.1.2)
  • Now add two URL rewrite rules to the <rules> section of the web.config file in the Dynamics CRM installation directory (by default, C:\Program Files\Microsoft Dynamics CRM\CRMWeb). 
  •      <rule name="Redirect for Internal" stopProcessing="true">  
             <match url="(.*)" />  
             <conditions trackAllCaptures="true">  
                 <add input="{HTTP_HOST}" pattern="(^icrm\.)|(^auth\.)|(^dev\.)" negate="true" />  
                 <add input="{LOCAL_ADDR}" pattern="10\.1\.1\.1" />  
                 <add input="{HTTP_HOST}" pattern="([^\.]*)" />  
             </conditions>  
             <action type="Redirect" url="https://icrm.mycompany.com/{C:1}/{R:1}" appendQueryString="true" logRewrittenUrl="true" redirectType="Found" />  
         </rule>  
         <rule name="Redirect for External" stopProcessing="true">  
             <match url="([^/]*)(.*)" />  
             <conditions trackAllCaptures="true">  
                 <add input="{LOCAL_ADDR}" pattern="10\.1\.1\.2" />  
                 <add input="{HTTP_HOST}" pattern="^icrm\." />  
                 <add input="{HTTP_HOST}" pattern="([^\.]*)(.*)" />  
             </conditions>  
             <action type="Redirect" url="https://{R:1}{C:2}{R:2}" appendQueryString="true" logRewrittenUrl="true" redirectType="Found" />  
         </rule>  
    


End Result

On the internal network 

A user enters https://icrm.mycompany.com/OrgX in the browser, neither url rewrite rules are matched, user is brought through to https://icrm.mycompany.com/OrgX

A user enters https://OrgX.mycompany.com in the browser, the "Redirect for Internal" rewrite rule is matched and user is redirected to https://icrm.mycompany.com/OrgX


On the external network 

A user enters https://icrm.mycompany.com/OrgX in the browser, the "Redirect for External" rewrite rule is matched and user is redirected to https://OrgX.mycompany.com

A user enters https://OrgX.mycompany.com in the browser, neither url rewrite rules are matched, user is brought through to https://OrgX.mycompany.com