{Dynamics CRM + Role Privilege} Record Privilege Checker for Dynamics CRM

Recently I had a requirement where our client’s support and maintenance team wanted a feature through which they can view the privileges for a record for all active users in the system. Ok, let me take a moment to explain here.

Suppose the support team wants to run a regular security check and identify what privilege does a specific user have on specific account records. You might be thinking that is that this hard? After all we can go to the user and identify the security roles of the user. And then determine what access privilege does the security role have on the account

Simple isn’t it? Well it is not. And specially with CRM 2016, a user might end up getting a specific privilege on record through any of the following.

  • Security roles
  • Sharing
  • Access teams
  • Owner team association
  • Hierarchical relationship.

It can be really complex depending on how your system is configured. Then what is the other way of finding out without user intervention. The customer did not want any external utility. They wanted something within CRM from where they could view the information.

So I developed a quick solution and then I thought why not share the community so that anybody in the future having the same requirement can benefit from this.

The tool is available for download for free at codeplex site – https://recordprivilegechecker.codeplex.com/releases/view/629363

 

Download the managed solution specific to your CRM version. Currently it supports

  • For CRM 2016 Version 8.1
  • For CRM 2016 Version 8.0
  • CRM 2015 Version

Once you install the managed solution, open any record form for any customizable entity. You should see a button called ‘Check User Access’ on the form.

image

Click on the button. Once you click on the button, a popup will show like the one below where you can view the privilege of all the active users on that particular record

image

 

And voila!,  it shows the privilege that each user has on the record. For Organization Owned entities it would show you the privilege as well.

image

 

Since ‘Assign’ and Share privileges are not valid for organization owned entities, you can see those privileges are blank.

The results might take some time to pop-up depending on the number of active users and access complexity in your environment.

And finally to round off, you can sort on the ‘FullName’ and ‘User Name’ columns. Also you have paging and searching to slice and dice your data.

image

 

Liked this tool? You can make a small donation at paypal account – debajit.prod@gmail.com. Your support and encouragement will help me to make many other tools in the future.

 

Till then you read one of my blog posts, happy CRM’ing.

Advertisements

{Dynamics 365/ CRM forms} Programmatically access controls from a system form of an entity in Dynamics CRM

It’s been sometime I have published a blog article as I am in neck deep work in some tool development. But still I could not resist myself from penning down this interesting stuff which I came across recently and share with all my readers.

Recently I came across a customer requirement where they needed to fetch all the controls in a tab in on a form for contact entity and show them in one of their external applications which is interacting with Dynamics CRM. I think an example here would explain the scenario.

I have a tab with display name – ‘DEMO TAB’ and name – ‘DEMO_TAB’ on the ‘Contact’ form of the contact entity.

image

The DEMO TAB has the following controls of the specified type

First Name Single line of text (type = simple)
Last Name Single line of text (type=simple)
Lead Source Optionset
Birthday DateTime
Originating Lead Lookup
Do not allow faxes two options
Credit Limit Currency

 

I haven’t taken all the data types and surely at the end of this blog you will get an idea on how to proceed with other data types as well.

So let’s see what are the  steps here to achieve the customer requirement.

  • Query the system form entity for contact entity with form name = ‘Contact’
  • Get the formxml
  • Parse the formxml to get all the controls within the DEMO TAB of the form
  • Finally render the controls in the custom application as per the control type. So First name should be rendered as textbox, Birthday probably as DatePicker control and Lead Source as dropdown.

For readers who are aware of ADX portals and how they work, you might have a question coming, isn’t is available OOB? Well it is and ADX is much more than this. We tried to put our point through for it but this is one off requirement for the customer and they have been using their custom application for pretty long time and hence did not want to discontinue the same.

Well, so we are back to our custom development.

Let’s complete our requirement step by step. So lets first write the piece of code to get the controls within the ‘DEMO TAB’ of the ‘CONTACT’ form of contact entity.

 

var systemForm = GetFormByName("contact", 2, proxy);
var controls = GetTabControls("DEMO_TAB", systemForm.GetAttributeValue<string>("formxml"), proxy);

private static Entity GetFormByName(string formName, int entityTypeCode, IOrganizationService service)
        {
            var query = new QueryExpression("systemform");
            query.Criteria.AddCondition("name", ConditionOperator.Equal, formName);
            query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, entityTypeCode);
            query.ColumnSet.AddColumn("formxml");

            var results = service.RetrieveMultiple(query);

            return results.Entities[0];
        }

        private static List<XElement> GetTabControls(string tabName, string formXml, IOrganizationService service)
        {
            var controlsList = new List<XElement>();
            var formDoc = XDocument.Parse(formXml);
            var tabs = formDoc.Descendants("tab");

            foreach (var tab in tabs)
            {
                if(tab.Attribute("name").Value.Equals(tabName, StringComparison.OrdinalIgnoreCase))
                {
                    // get all the controls.
                    controlsList = tab.Descendants("control").ToList();
                    break;
                }
            }

            return controlsList;
        }

 

The first method retrieves the form for the contact entity and the second method extracts all the controls within the ‘DEMO_TAB’.

I have used System.Xml.Linq and believe me, it is much cleaner way to parse your xml than using the traditional System.Xml.Xmldocument methods.

So we now have the controls. Now how to get the field name and the type of the control? To explore this, lets see what the form xml looks like. I have pasted a portion of the tab xml

<tab name="DEMO_TAB" id="{596d8090-3b20-77ad-3c36-77442a42835c}" IsUserDefined="0" locklevel="0" showlabel="true" expanded="true">
            <labels>
                <label description="DEMO TAB" languagecode="1033" />
            </labels>
            <columns>
                <column width="100%">
                    <sections>
                        <section name="tab_4_section_1" showlabel="false" showbar="false" locklevel="0" id="{a64c108b-c1d1-1d90-ebd0-e2c4ab7ced3d}" IsUserDefined="0" layout="varwidth" columns="11" labelwidth="115" celllabelalignment="Left" celllabelposition="Left">
                            <labels>
                                <label description="Section" languagecode="1033" />
                            </labels>
                            <rows>
                                <row>
                                    <cell id="{2b74bc94-1e03-4ab7-428e-8283732c57ca}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="First Name" languagecode="1033" />
                                        </labels>
                                        <control id="firstname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="firstname" disabled="false" />
                                    </cell>
                                    <cell id="{b6d61c95-0c1d-4cae-2e7a-1210e4d7a1cd}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Lead Source" languagecode="1033" />
                                        </labels>
                                        <control id="leadsourcecode" classid="{3EF39988-22BB-4f0b-BBBE-64B5A3748AEE}" datafieldname="leadsourcecode" disabled="false" />
                                    </cell>
                                </row>
                                <row>
                                    <cell id="{b06bd53b-c215-d372-0265-e3da223d9bd5}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Last Name" languagecode="1033" />
                                        </labels>
                                        <control id="lastname" classid="{4273EDBD-AC1D-40d3-9FB2-095C621B552D}" datafieldname="lastname" disabled="false" />
                                    </cell>
                                    <cell id="{6780cace-3301-540e-0425-543755db1a18}" showlabel="true" locklevel="0">
                                        <labels>
                                            <label description="Originating Lead" languagecode="1033" />
                                        </labels>
                                        <control id="originatingleadid" classid="{270BD3DB-D9AF-4782-9025-509E298DEC0A}" datafieldname="originatingleadid" disabled="false" />
                                    </cell>
                                </row>
                                <row>

See for the section highlighted in yellow. Let’s see what we can get from there.

  • description – the display name for the field
  • datafieldname – the logical name of the field
  • disabled – value to indicate whether the field is enabled or disabled on the form.

So far so good. We have all the necessary information. But wait? How do we get the type of the field and decide whether to render it as textbox or dropdown or datepicker?

You have two methods here. One is to query the metadata based on the field name (datafieldname) and then determine the type and render the control accordingly. If you ask me, this is probably the best way to determine to do since it would be independent of any version changes or upgrades in CRM.

But one disadvantage is the amount of time it takes to query the metadata is significant and your page rendering time could go for a toss depending on how many controls you are querying for.

The other and probably faster way to do this is to use the classid attribute of the control. Check for the stuff highlighted in green. If you observe carefully both first name and last name have the same data type and their classid is same as well. It’s more like each control type being identified by a separated id. However this method has major caveats

  • You would need to identify the classid of all type and probably use them in your code, perhaps as switch-case.
  • If for some reason, Microsoft changes the classid of the control types, your code would go for a toss.
  • Finally, with new releases, Dynamics is coming up with new field types and hence you need to keep your code changing for the new types as they come.

Wondering how to get the attributes of the controls. Well below is the code to help you on that.

foreach (var control in controls)
            {
                var isDisabled = control.Attribute("disable").Value;
                var classId = control.Attribute("classid").Value;
                var datafieldName = control.Attribute("datafieldname").Value

                // parse the other attributes
            }

 

Isn’t it easy with System.Xml.Linq?

 

Hope you find this useful.

{Dynamics CRM/ 365 + Views}–Create a view to show security roles assigned to user along with roles the user inherits from the team

The title might be confusing. So let’s straightway jump to the requirement here. Our client a role lookup on custom entity form and on click of the lookup, the user should be presented with a view with a view which shows the security role directly assigned to the user along with the security roles the user inherits from the team.

For e.g.

User A has security role – Role 1

User A is a member of the team which has security role – Role 2

So the view would show both the roles – Role 1 + Role 2

Very simple requirement right? However can you achieve this using OOB view? Well unfortunately the answer is BIG NO.

We went ahead with the design that when a user clicks the lookup we would call dynamically a fetchxml using the OOB custom view. Now the big task is creating the fetch xml of the custom view.

After much deliberation, I finally came up with the below Fetch XML which serves exactly the purpose. So let’s see our protagonist in the play.

 

var fetchString = string.Format(@"<fetch version=’1.0′ output-format=’xml-platform’ mapping=’logical’ distinct=’true’>
                                            <entity name=’role’>
                                                <attribute name=’name’ />
                                                <attribute name=’businessunitid’ />
                                                <attribute name=’roleid’ />
                                                <order attribute=’name’ descending=’false’ />
                                                <link-entity name=’systemuserroles’ from=’roleid’ to=’roleid’ link-type=’outer’>
                                                    <link-entity name=’systemuser’ from=’systemuserid’ to=’systemuserid’ alias=’aj’ link-type=’outer’>
                                                        <attribute name=’systemuserid’ />
                                                    </link-entity>
                                                </link-entity>
                                                <link-entity name=’teamroles’ from=’roleid’ to=’roleid’ visible=’false’ intersect=’true’ link-type=’outer’>
                                                    <link-entity name=’teammembership’ from=’teamid’ to=’teamid’ visible=’false’ intersect=’true’ link-type=’outer’>
                                                        <link-entity name=’systemuser’ from=’systemuserid’ to=’systemuserid’ alias=’al’ link-type=’outer’ >
                                                            <attribute name=’systemuserid’ />
                                                        </link-entity>
                                                    </link-entity>
                                                </link-entity>
                                                    <filter type=’or’>
                                                        <condition entityname=’aj’ attribute=’systemuserid’ operator=’eq’ value='{0}’ />
                                                        <condition entityname=’al’ attribute=’systemuserid’ operator=’eq’ value='{0}’ />
                                                    </filter>

                                            </entity>
                                        </fetch>", userid);

I have highlighted the important part of the query. The first thing to notice here is the alias that I provided while fetching the roles from the systemuserroles table and the teamroles table.

The next part is using the alias and constructing the filter condition with the alias as the entity name. userid is the GUID of the user whose security roles needs to be determined.

 

Please note that this type of construct is actually possible from CRM 2013 and onwards. Although this works, unfortunately this construct is not supported through the UI.

Small trick but it can consume days if not know.

Hope this helps! Till you read one or more of my blog posts, happy CRM’ing

{Dynamics CRM + Upsert} Execute Upsert request from client side in Dynamics CRM

Upsert request was introduced in Dynamics CRM 2015 Online Update 1 and on-premise customers got a flavour of it from CRM 2016 onwards. After this feature has been introduced, it has been used so much that writing anything on it is now hackneyed.

However just when you feel you have done it 100 times, you may still find that one scenario suddenly popping up which you might have not implemented in your previous projects and this was exactly one of those. My client wanted to fire an Upsert request from the client side. Basically the requirement was, there was a webresource where the user would fill the necessary information for a record and based on the field values entered, an Upsert request would create/ update the record accordingly.

So what do I need here? Well the first thing I need here is the XML request. And how do I get that?  Did you forget about the wonderful SOAP Logger tool that ships with SDK. Yeah and it still works great. Smile

By any chance you are not aware where you can find the SOAP Logger, you can find in your SDK Download in the following folder structure – SDK\SampleCode\CS\Client\SOAPLogger

Let’s take an example here. For account entity, first I create an alternate key with combination of Account Number + Account Name.

image

I open the SOAP logger and run the below code to get the corresponding XML request for Upsert request

SoapLoggerOrganizationService slos = new SoapLoggerOrganizationService(serverConfig.OrganizationUri, service, output);

                              //Add the code you want to test here:
                              // You must use the SoapLoggerOrganizationService ‘slos’ proxy rather than the IOrganizationService proxy you would normally use.

                              KeyAttributeCollection acckeys = new KeyAttributeCollection();

                              acckeys.Add("accountnumber", "ASH001");
                              acckeys.Add("name", "SOAP Logger for Test");
                              Entity account = new Entity("account", acckeys);
                              account["name"] = "SOAP Logger for Test";

                              UpsertRequest upsert = new UpsertRequest();
                              upsert.Target = account;
                              UpsertResponse response = (UpsertResponse)slos.Execute(upsert);

 

Once I run the above code, the below is the XML Request

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <Execute xmlns="
http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <request i:type="a:UpsertRequest" xmlns:a="
http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:Parameters xmlns:b="
http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
            <b:key>Target</b:key>
            <b:value i:type="a:Entity">
              <a:Attributes>
                <a:KeyValuePairOfstringanyType>
                  <b:key>name</b:key>
                  <b:value i:type="c:string" xmlns:c="
http://www.w3.org/2001/XMLSchema">SOAP Logger for Test</b:value>
                </a:KeyValuePairOfstringanyType>
              </a:Attributes>
              <a:EntityState i:nil="true" />
              <a:FormattedValues />
              <a:Id>00000000-0000-0000-0000-000000000000</a:Id>
             <a:KeyAttributes xmlns:c="
http://schemas.microsoft.com/xrm/7.1/Contracts">
                <c:KeyValuePairOfstringanyType>
                  <b:key>accountnumber</b:key>
                  <b:value i:type="d:string" xmlns:d="
http://www.w3.org/2001/XMLSchema">ASH001</b:value>
                </c:KeyValuePairOfstringanyType>
                <c:KeyValuePairOfstringanyType>
                  <b:key>name</b:key>
                  <b:value i:type="d:string" xmlns:d="
http://www.w3.org/2001/XMLSchema">SOAP Logger for Test</b:value>
                </c:KeyValuePairOfstringanyType>
              </a:KeyAttributes>
              <a:LogicalName>account</a:LogicalName>
              <a:RelatedEntities />
              <a:RowVersion i:nil="true" />
            </b:value>
          </a:KeyValuePairOfstringanyType>
        </a:Parameters>
        <a:RequestId i:nil="true" />
        <a:RequestName>Upsert</a:RequestName>
      </request>
    </Execute>
  </s:Body>
</s:Envelope>

This request is just like any other request for Create/ Update except for the highlighted part. If you notice carefully, you could find a new tag in XML request name KeyAttributes. All you need to do is replace the values of the KeyAttributes

Here is the sample code for the same in javascript

var accountNumber = ‘ASH001’;
var accountName = ‘SOAP Logger for Test’;

var request = [‘<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">’,
  ‘<s:Body>’,
    ‘<Execute xmlns="
http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">’,
      ‘<request i:type="a:UpsertRequest" xmlns:a="
http://schemas.microsoft.com/xrm/2011/Contracts">’,
        ‘<a:Parameters xmlns:b="
http://schemas.datacontract.org/2004/07/System.Collections.Generic">’,
          ‘<a:KeyValuePairOfstringanyType>’,
            ‘<b:key>Target</b:key>’,
            ‘<b:value i:type="a:Entity">’,
              ‘<a:Attributes>’,
                ‘<a:KeyValuePairOfstringanyType>’,
                  ‘<b:key>name</b:key>’,
                  ‘<b:value i:type="c:string" xmlns:c="
http://www.w3.org/2001/XMLSchema">’, accountName, ‘</b:value>’,
                ‘</a:KeyValuePairOfstringanyType>’,
              ‘</a:Attributes>’,
              ‘<a:EntityState i:nil="true" />’,
              ‘<a:FormattedValues />’,
              ‘<a:Id>00000000-0000-0000-0000-000000000000</a:Id>’,
              ‘<a:KeyAttributes xmlns:c="
http://schemas.microsoft.com/xrm/7.1/Contracts">’,
                ‘<c:KeyValuePairOfstringanyType>’,
                  ‘<b:key>accountnumber</b:key>’,
                  ‘<b:value i:type="d:string" xmlns:d="
http://www.w3.org/2001/XMLSchema">’, accountNumber,'</b:value>’,
                ‘</c:KeyValuePairOfstringanyType>’,
                ‘<c:KeyValuePairOfstringanyType>’,
                  ‘<b:key>name</b:key>’,
                  ‘<b:value i:type="d:string" xmlns:d="
http://www.w3.org/2001/XMLSchema">’, accountName, ‘</b:value>’,
                ‘</c:KeyValuePairOfstringanyType>’,
              ‘</a:KeyAttributes>’,
              ‘<a:LogicalName>account</a:LogicalName>’,
              ‘<a:RelatedEntities />’,
              ‘<a:RowVersion i:nil="true" />’,
            ‘</b:value>’,
          ‘</a:KeyValuePairOfstringanyType>’,
        ‘</a:Parameters>’,
        ‘<a:RequestId i:nil="true" />’,
        ‘<a:RequestName>Upsert</a:RequestName>’,
      ‘</request>’,
    ‘</Execute>’,
  ‘</s:Body>’,
‘</s:Envelope>’].join(”);

var req = new XMLHttpRequest();

req.open("POST", Xrm.Page.context.getClientUrl() + "/XRMServices/2011/Organization.svc/web", false);
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction",
http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute);

req.send(request);

console.log(req.responseText);

 

The below is the response text.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <ExecuteResponse xmlns="
http://schemas.microsoft.com/xrm/2011/Contracts/Services">
      <ExecuteResult i:type="a:UpsertResponse" xmlns:a="
http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:ResponseName>Upsert</a:ResponseName>
        <a:Results xmlns:b="
http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
      
     <b:key>RecordCreated</b:key>
            <b:value i:type="c:boolean" xmlns:c="
http://www.w3.org/2001/XMLSchema">false</b:value>
          </a:KeyValuePairOfstringanyType>
          <a:KeyValuePairOfstringanyType>
            <b:key>Target</b:key>
            <b:value i:type="a:EntityReference">
              <a:Id>138f7d7a-1f48-e611-80e9-5065f38be1c1</a:Id>
              <a:KeyAttributes xmlns:c="
http://schemas.microsoft.com/xrm/7.1/Contracts"/>
              <a:LogicalName>account</a:LogicalName>
              <a:Name i:nil="true"/>
              <a:RowVersion i:nil="true"/>
            </b:value>
          </a:KeyValuePairOfstringanyType>
        </a:Results>
      </ExecuteResult>
    </ExecuteResponse>
  </s:Body>
</s:Envelope>

 

In the response, you can find whether the record is created or updated. Please see the highlighted section. I already have an account with same accountnumber and name and hence RecordCreated value has been set to false

 

Hope this helps!

{Dynamics CRM + Web API Actions} Execute action with an Entity as input parameter using Web API in Dynamics CRM 2016

“Executing action using Web API” – well this is a topic which has become banal. It’s been talked about, people are using it every now and then in their projects. After all 2016 has just swayed away the consultants and customers alike. In-fact I wrote a blog on the same topic almost 6 months back in January when CRM 2016 was in it’s early days.

https://debajmecrm.com/2016/01/06/invoke-your-custom-action-from-dynamics-crm-web-apidynamics-crm-2016/

Although this blog post was much appreciated and I cannot thank the readers more for this, I have getting repeated questions about multiple scenarios related to this and one of them is – How do I execute a global action using Web API with an entity as input parameter. Mostly in the examples shared on the blog, the input parameters are boolean and string.

So I decided to pen it down here. So let’s take a very simple example here.

For this demo, I have created an action named ‘Test Action’. That is a global action with Entity as the input parameter. The Entity type is account.

image

Now coming to the code. Below is the sample code to do the same.

function callAction() {

    var organizationUrl = Xrm.Page.context.getClientUrl();

    var account = {};
    account.name = "Test Account Name";
    account.description = "This account was created for action test.";
    account.revenue = 2000000.00; // decimal
    account.donotphone = true; //boolean field
    //account.logicalname = "account";
    var data = { "EntityArg": account };

    var query = "new_TestAction";
    var req = new XMLHttpRequest();
    req.open("POST", organizationUrl + "/api/data/v8.0/" + query, true);
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.onreadystatechange = function () {
        if (this.readyState == 4) {
            req.onreadystatechange = null;
            if (this.status == 200) {
                var data = JSON.parse(this.response);

            } else {
                var error = JSON.parse(this.response).error;
                alert(error.message);
            }
        }
    };

    req.send(window.JSON.stringify(data));
}

 

What the sample code is doing here? Well this extremely complex code is dynamically creating an account and then passing it. You can however retrieve an existing account and pass it as well. However here I am creating the account dynamically and passing it.

Simple piece of code. Hope this helps!

{Dynamic CRM Word templates} Setting word templates using workflow not working for custom entity in Dynamics CRM 2016? Check this out

Document template feature of Dynamics CRM 2016 came as a big relief to many consultants like us whose only option prior to that was to go to customer and speak about mail merge and many a times which was not liked by customers much. However the introduction of word and excel template features in Dynamics CRM 2016 never ceases to amaze the customers and has made the life of consultants like us much easier.

Now coming to word templates, one big disadvantage while accomplishing from the CRM UI is that it cannot be done for multiple records. You have to do for each individual record which is quite cumbersome right?

Well you have a quick fix for that. There is a new step introduced in the workflow called Perform Action Step and using that you can actually set the word template for an entity. So basically the idea is you can have an on-demand workflow which will use this step to set the word template to the target entity type and then coming back to the UI, you can select multiple records and run the workflow. And your very complex workflow will attach the word template to each of the records you selected the workflow to run. You could view the the attachment in the annotations section for each record.

image

 

image

 

image

The above pics are taken from Inogic blog on the same article. It is a wonderful article which has explained the same approach that I highlighted above.  Just like all their other articles, this one is also a treat to read. – http://www.inogic.com/blog/2016/02/set-word-template-through-workflow-in-dynamics-crm-2016/

Well so far so good. But wait, are we missing a link here. Hell yeah!

All the examples you find on this topic are all using OOB entities and indeed all the examples work great. But to be honest, in most of the cases we end up designing lot of custom entities for the customer. So what if we need to do something similar for the custom entities. Well then you have a problem. Let’s explore that.

I have created a custom entity called Template Test. I created a global word template for the custom entity. Now I create a workflow of on-demand type which will set the word template to the custom entity as the target.

As usual, I select the Perform Action –> Set word template –> Click on Set properties –> Select the appropriate word template for the entity

image

image

Oops! What happened here? The problem here is that while selecting the target, the entity is not showing up in the Form Assistant like the one it shows for OOB entities like Account and Contact.

 

Scratching your head here to identify the issue. Well I have two solutions for you as a workaround for this and I leave this up to you to identify which one fits best for you

 

Sol 1: Enable Business Process Flows for the custom entity:

As weird it may sound, enabling business process flow for the custom entity would indeed make your entity appear in the form assistant for the target field.

Go to your entity customization and check the checkbox for business process flow fields creation.

image

Save and publish the entity customizations. Now come back to the workflow created earlier and click on the target field. You should be able to set it to the custom entity.

image

You actually do not need to create the business process flows for the entity. Just enabling it to support business process flows would do the trick for you.

 

Sol 2: Programmatically set the word template for your target entity

If the above solution is not your cup of tea, then you can do something like below:

  • Create a on-demand workflow for your custom entity which would invoke a custom workflow assembly.
  • The custom workflow assembly would set the word template programmatically using the below code.

OrganizationRequest req = new OrganizationRequest("SetWordTemplate");
req["Target"] = new EntityReference("<custom entity schema name>", customentityguid);
req["SelectedTemplate"] = new EntityReference("documenttemplate", templateid);
orgService.ExecuteCrmOrganizationRequest(req);

A small stuff but enough to waste your days researching on the same.

 

Hope this helps!

{Dynamics CRM + SAML + ADFS} FParse SAML Assertion token generated from Dynamics CRM on-premise environment configured with Claims

This blog post is a continuation of my previous post in which I explained on how to get SAML Token programmatically for your Dynamics On-premise environment configured with claims. If you have not read the previous post, I strongly suggest to read it and come back to this.

https://debajmecrm.com/2016/06/05/utility-saml-token-generator-for-your-dynamics-crm-online-configured-with-claims/

 

In this blog post, I will show you how to read the SAML assertion token that was obtained in the previous step. This would be typically done by applications who are relying on your SAML token to authenticate the user and many cases would be handled by a separate. But knowing a bit more never really harms. So if you are interested, let’s proceed.

Our main intention here would be to parse the SAML token and get the user’s claims from that. But there is a problem here. The SAML assertion token would be encrypted by the certificate of your relying party. So the decrypting party would need to have the certificate to decrypt the SAML token. Just to give you an idea, the SAML assertion token would have the following format.

<samlp:Response ID=’_4172503e-7104-4382-8cda-6f93f65de530′ Version=’2.0′ IssueInstant=’2016-06-01T05:05:18.362Z’ Destination=https://SAMLAssertionendpoint” Consent=’urn:oasis:names:tc:SAML:2.0:consent:unspecified’ xmlns:samlp=’urn:oasis:names:tc:SAML:2.0:protocol’>
                              <Issuer xmlns=’urn:oasis:names:tc:SAML:2.0:assertion’>
/adfs/services/trust">http://<federation serverl url>/adfs/services/trust</Issuer>
                              <samlp:Status>
                                <samlp:StatusCode Value=’urn:oasis:names:tc:SAML:2.0:status:Success’ />
                              </samlp:Status>
                              <EncryptedAssertion xmlns=’urn:oasis:names:tc:SAML:2.0:assertion’>
                                <xenc:EncryptedData Type=’
http://www.w3.org/2001/04/xmlenc#Element’ xmlns:xenc=’http://www.w3.org/2001/04/xmlenc#’>
                                  <xenc:EncryptionMethod Algorithm=’
http://www.w3.org/2001/04/xmlenc#aes256-cbc’ />
                                  <KeyInfo xmlns=’
http://www.w3.org/2000/09/xmldsig#’>
                                    <e:EncryptedKey xmlns:e=’
http://www.w3.org/2001/04/xmlenc#’>
                                      <e:EncryptionMethod Algorithm=’
http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p’>
                                        <DigestMethod Algorithm=’
http://www.w3.org/2000/09/xmldsig#sha1′ />
                                      </e:EncryptionMethod>
                                      <KeyInfo>
                                        <ds:X509Data xmlns:ds=’
http://www.w3.org/2000/09/xmldsig#’>
                                          <ds:X509IssuerSerial>
                                            <ds:X509IssuerName><—Removed certificate details—-></ds:X509IssuerName>
                                            <ds:X509SerialNumber>2787593233934917942755289290276949976521129800</ds:X509SerialNumber>
                                          </ds:X509IssuerSerial>
                                        </ds:X509Data>
                                      </KeyInfo>
                                      <e:CipherData>
                                        <e:CipherValue>gj65thkZ4rB22ktgO6KdTsWLhyUy5gpvD2Jt94on4Z6ILT2VbpW/sMyFUVmdsY0dJ1nk8ijyhTt2w7T4HJf3Gl2OzB0LZm8S0KrjYtDGTUQetT08SI1puaXmN0JQMeiGWio/qyuJ8bUMGwMJnQbkZ6U/RMmICN8WQlErNI/ALux7n3SnI6kApA55UIP1OxnOglM6n8OzHwwnyCPBo6Je/impg3E5o1JtWC3tx6u2w2tOL3/kCWpZ9ELNMajcd8WeYXs8DAagHpoGVoqrmfL49dcnYhcWmEP+ROIRk6QerykmK1PVI3u0/mdCRAI6c+PlF4d2LPB9GZQyNGS78ch5UA==</e:CipherValue>
                                      </e:CipherData>
                                    </e:EncryptedKey>
                                  </KeyInfo>
                                  <xenc:CipherData>
                                    <xenc:CipherValue>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</xenc:CipherValue>
                                  </xenc:CipherData>
                                </xenc:EncryptedData>
                              </EncryptedAssertion>
                            </samlp:Response>

 

So let’s proceed with it.

  • Since Dynamics CRM is configured as your relying party, login to your Dynamics CRM server, open IIS –> Server Certificates –> Select the encryption certificate and right click – > select export option from the menu

image

  • Export it as .pfx file since you need the private key as well in the certificate to decrypt the SAML assertion. Since the certificate is exported as pfx file, you need to give a password here. Remember the password, this would be need later in code.

image

  • Click OK. The pfx file would be created in the location that you provided. Copy the .pfx file and store it in a location from  client application which consumes the SAML token, can access. The rest is very simple. Below is the sample code which would read the SAML assertion and get the claims back.

 

private static void SAMLParser()
                {
                        string samlFilePath = @"saml.xml";
                        string certificatePath = @"<certificate path>.pfx";
                        X509Certificate2 cert = new X509Certificate2(certificatePath, "<password when exported>");
                        var serviceTokens = new List<SecurityToken>();
                        serviceTokens.Add(new X509SecurityToken(cert));

                        var issuers = new ConfigurationBasedIssuerNameRegistry();
                        issuers.AddTrustedIssuer(cert.Thumbprint, cert.IssuerName.Name);

                        var configuration = new SecurityTokenHandlerConfiguration
                                    {
                                            AudienceRestriction = { AudienceMode = AudienceUriMode.Never },
                                            CertificateValidationMode = X509CertificateValidationMode.None,
                                            RevocationMode = X509RevocationMode.NoCheck,
                                            IssuerNameRegistry = issuers,
                                            MaxClockSkew = TimeSpan.FromMinutes(5),
                                            ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false)

                                    };

                        var tokenHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);

                        using (StringReader sr = new StringReader(XDocument.Load(samlFilePath).ToString()))
                        {
                                using (XmlReader reader = XmlReader.Create(sr))
                                {
                                        if (!reader.ReadToFollowing("EncryptedAssertion", "urn:oasis:names:tc:SAML:2.0:assertion"))
                                        {
                                                throw new Exception("Assertion not found!");
                                        }
                                        SecurityToken token = tokenHandlers.ReadToken(reader.ReadSubtree());

                                        Saml2SecurityToken samlToken = token as Saml2SecurityToken; // Getting the SAML token here.

                                        // This is where you get the claims of the user from the SAML assertion token

                                        Dictionary<string, object> payload = new Dictionary<string, object>();
                                        foreach (Saml2Statement statement in samlToken.Assertion.Statements)
                                        {
                                                Saml2AttributeStatement attributeStatement = statement as Saml2AttributeStatement;
                                                if (null != attributeStatement)
                                                {
                                                        foreach (Saml2Attribute attribute in attributeStatement.Attributes)
                                                        {
                                                                Console.WriteLine("Name: {0}, Value: {1} ValueType: {2}", attribute.Name, attribute.Values[0], attribute.AttributeValueXsiType);
                                                                payload.Add(attribute.Name, attribute.Values[0]);

                                                        }
                                                }
                                        }

                                    // Use this to convert it into JWT token and pass on to the web-api.

                                }
                        }
                }

Since you are able to decrypt and find out the claims, you application assumes that it is being originated from your Dynamics CRM.

You can use these claims and then convert it into JWT token.

 

Hope this helps!

{Dynamics CRM + SAML + ADFS}–Get SAML Token programmatically for your Dynamics On-premise environment configured with claims

As a CRM Consultant we come across myriad of requirements. Of them some are our day-day stuffs. But the ones which puts us in a bit of head scratching and brainstorming are the ones which gives us the maximum satisfaction when completed. And this was one of them.

Our client had configured IFD for their Dynamics CRM 2015 on-premise environment. They had a WebAPI web service to which CRM needed to communicate. After initial discussions with the customer, the idea was to pass the SAML token from Dynamics CRM as one of header values in the web api call. The WebAPI would then parse the SAML token from the header of the request and then read the claims to authenticate the caller and send the response back to CRM.

So first things first. We have to pass the SAML token as one of the header values in the web-api call. How to approach this. Let’s understand the mechanism here. For people who already know this, it’s like a repeat but for readers who are unaware of this, I will try to put it in very simple terms.

  • CRM is configured as a relying party fro ADFS. So CRM will only trust only tokens generated from ADFS
  • User tries to login to Microsoft Dynamics CRM. Since user could not present a valid SAML token to CRM, CRM redirects the user to ADFS login page.
  • User enters the username and password. ADFS has active directory configured as trust store. It takes your userid and password and validates it against the identity provider which is your active directory.
  • IF the credentials are correct, Active directory issues a token which contains the claims for the user. ADFS then converts the claims and puts into the SAML token with the claims which are understood by your Dynamics CRM. The user now sends another request to Dynamics CRM and this time with a valid SAML token. CRM now allows the user login.

 

Believe me this is explained in a very simple way. However there are whole lot of stuffs that you need to configure for this to take place. If you are interested to know the gory details, you can visit the technet article – https://technet.microsoft.com/en-us/library/dn609803.aspx

Now coming back to our topic. Since our intention is to get the SAML token here, if we take an analogy, we have to replicate the whole process as above but without user intervention. Wait? What is the problem then? The problem the user’s credentials which needs to be passed to get the SAML token. How can we do this then?

The solution is to use execution users credentials and to do that we have to replicate the sign-in process. To do that the first thing make the user hit the SignOn page of ADFS.

The signon page has the url of https://<your adfs server url>/adfs/ls/idpinitiatedsignon.aspx

The next thing we need to do is create a SAML asserting endpoint for your relying party if that is already not present. To do this, follow the below steps

  • Open your ADFS server and then open ADFS Management window and click on relying parties.

image

  • Select the Relying Party trust for your CRM and then right click-> Properties  and go to EndPoints.

image

  • Check if you have SAML Assertion end-point showing up. If not then click on Add and enter a new SAML assertion end-point. Binding as POST and Assertion Endpoint with the same URL as your relying party.

 

image

  • Save and close out the Dialog.

Don’t worry. You ADFS configuration will not go wrong here. The Assertion end-point would work in conjunction with the WS-Federation end-points.

So that’s it you are all done. Now comes the coding part to get the SAML token. Well I have covered your back here. I have put the gory code into a managed solution and all you need to do is download the solution from the below codeplex link and then install it into your organization – https://crmsamlgenerator.codeplex.com/

Once installed, the following is the piece of code which you just need to write to get back your SAML assertion token.

OrganizationRequest samlInvoker = new OrganizationRequest();
samlInvoker.RequestName = "saml_SAMLInvoker";
samlInvoker.Parameters["IdpLoginPageUrl"] = “
/adfs/ls/idpinitiatedsignon.aspx";’>https://<federation server url>/adfs/ls/idpinitiatedsignon.aspx";
samlInvoker.Parameters["RelyingPartyUrl"] = "<Relying Party URL>";

var samlResponse = (OrganizationResponse)proxy.Execute(samlInvoker);

var samlParams = samlResponse.Results;

Console.WriteLine("SAML Token XML: {0}", samlParams["SamlTokenXml"]);

 

As you can see from the code above, all you need to do is invoke an action named “saml_SAMLInvoker” with the parameters I have mentioned before and you get the SAML token in XML format back. You call this from javascript too and get this token.

You can pass this to any any web-api call from javascript or server side call. A sample code for passing the SAML XML token in the header value is illustrated below.

_invokeWebAPI: function (samlToken) {
        var req = new XMLHttpRequest();
        req.open("GET", "<your web api query");
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.setRequestHeader("SAML", samlToken);
        req.onreadystatechange = function () {
            if (this.readyState == 4 /* complete */) {
                req.onreadystatechange = null;
                if (this.status == 200) {
                    var discovery = JSON.parse(this.response);
                }
                else {
                    var error = JSON.parse(this.response).error;
                }
            }
        };
        req.send();

In my next article, I have explained how to parse the SAML Assertion token – https://debajmecrm.com/2016/06/06/parse-saml-assertion-token-generated-from-dynamics-crm-on-premise-environment-configured-with-claims/

Till then happy CRMing.