Tuesday, 28 January 2014

Error: ID4175: The issuer of the security token was not recognized by the IssuerNameRegistry in Ms Dynamics CRM 2011

Came back from a long weekend yesterday to be greeted by a non working development environment. It was prompting for credentials when logging in to Dynamics CRM. Thus, after having done what any self respecting developer would've done, namely bounce IIS, I decided to have a look at the logs, where I found this:
Error: ID4175: The issuer of the security token was not recognized by the IssuerNameRegistry. To accept security tokens from this issuer, configure the IssuerNameRegistry to return a valid name for this issuer.
Error Message: ID4175: The issuer of the security token was not recognized by the IssuerNameRegistry. To accept security tokens from this issuer, configure the IssuerNameRegistry to return a valid name for this issuer.

Source File: Not available

Line Number: Not available

Request URL: https://devcrm1.dev.local/default.aspx

Stack Trace Info: [SecurityTokenException: ID4175: The issuer of the security token was not recognized by the IssuerNameRegistry. To accept security tokens from this issuer, configure the IssuerNameRegistry to return a valid name for this issuer.]
at Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler.CreateClaims(SamlSecurityToken samlSecurityToken)
at Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler.ValidateToken(SecurityToken token)
at Microsoft.IdentityModel.Tokens.SecurityTokenHandlerCollection.ValidateToken(SecurityToken token)
at Microsoft.IdentityModel.Web.TokenReceiver.AuthenticateToken(SecurityToken token, Boolean ensureBearerToken, String endpointUri)
at Microsoft.IdentityModel.Web.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequest request)
at Microsoft.IdentityModel.Web.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args)
at Microsoft.Crm.Authentication.Claims.CrmFederatedAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Some sort of ADFS issue, I thought to myself. So I jumped onto the Dev ADFS box and found no correlated errors :(

I checked the Relying Party Trusts and the CRM one was fine, not showing any errors, I even went as far as using the test url facility, but alas, no dice.
During my general sweep of ADFS, I looked at the certificates and .... there were new certificates as the old ones were about to expire:


This means that the certificate thumbprint for token signing being used by Dynamics CRM is wrong and needs changing. This is trivial for web services, as all you need to do is change the bold line in the web.config to reflect the new certificate thumbprint.
 <microsoft.identityModel>
    <service name="MyCustom.Service.Something">
      <audienceUris>
        <add value="https://devcrm1.dev.local:1337/MyCustom.Service.Something/Service.svc" />    
      </audienceUris>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="5CE1DB0783B190D1CE4481B9C39CAD61AC6ACD56" name="http://adfs.dev.local/adfs/services/trust" />        </trustedIssuers>
      </issuerNameRegistry>
    </service>
  </microsoft.identityModel>

In MS Dynamics CRM things are perhaps simpler as there is no editing of the web.config file just a reconfiguration of Claims-Based Authentication (really, just run through the wizard as all other settings should already be correct):

1. Start the Deployment Manager:


2. Click Configure Claims-Based Authentication, which will launch the wizard and continue clicking next until the end of the Wizard.





3. Restart IIS.

Having done this, I was able to access my Dev environment again.

Friday, 24 January 2014

The Anti-Pattern Game

Last week a fellow IT professional and I, came up with The Anti-Pattern Game

The mechanics are very basic:

Wikipedia has a handy page with a number of Anti-Patterns

  • You add a point for each anti-Pattern listed in the Wikipedia article currently in use in your Company/Project.
  • You subtract a point for each anti-Pattern listed in the Wikipedia article that was used in your Company/Project but no longer is.

You get bonus points for anti-patterns in use but not listed in the article and not covered by the ones listed.

So how did my project do?

  • Escalation of commitment: Failing to revoke a decision when it proves wrong.
  • Management by perkele: Authoritarian style of management with no tolerance of dissent.
  • Management by objectives: Management by numbers, focus exclusively on quantitative management criteria, when these are non-essential or cost too much to acquire.
  • Mushroom management: Keeping employees uninformed and misinformed; employees are described as being kept in the dark and fed manure, left to stew, and finally canned.
  • Avalanche: An inappropriate mashup of the Waterfall model and Agile Development techniques.
  • Scope Creep: Uncontrolled changes or continuous growth in a project’s scope, or adding new features to the project after the original requirements have been drafted and accepted. (Also known as requirement creep and feature creep.)
  • Bystander apathy: When a requirement or design decision is wrong, but the people who notice this do nothing because it affects a different group of people.
  • Stovepipe system: A barely maintainable assemblage of ill-related components.
  • Boat anchor: Retaining a part of a system that no longer has any use.
  • Error hiding: Catching an error message before it can be shown to the user and either showing nothing or showing a meaningless message. Also can refer to erasing the Stack trace during exception handling, which can hamper debugging.
  • Lasagna code: Programs whose structure consists of too many layers.
  • Tester Driven Development: Software projects in which new requirements are specified in bug reports.

We've also dabbled with the crunch mode anti-pattern but it seems on as needed basis, thus not sure how to score it, so I have left it out.

In red is an anti-pattern the project no longer engages in.

My current project achieved a respectable: 10

To be fair, some of the anti-patterns are direct results of the technology used but still, 10 seems like a respectable score.

What about yours?

Monday, 20 January 2014

Turn off IE Compatibility mode in MS Dynamics CRM 2011 or why indexOf doesn't work.

We'd been having navigation issues in MS Dynamics CRM 2011 for a while: Mouse scrolling wouldn't work and double clicking was necessary to select anything from a drop down menu. The issue would disappear if IE was using IE 9 document standards. The problem was that IE would "downgrade" itself to IE 7.

Somewhat bizarrely this wasn't occurring everywhere, it was just occurring in one of our environments causing loads of headaches for everybody. The testers were annoyed that they had, effectively limited functionality and kept forgetting to set document standards to IE 9 via developer tools. We weren't sure why this was happening in only one environment.

Things came to a head last week when I used a variation of my function getAllUserRoles and due to indexOf only being supported on IE > 9, it was working everywhere apart from on this environment. 

It turns out that there is a setting, introduced in UR12, called Load Pages in the most recent version of Internet Explorer, which modifies the headers so that the pages are rendered in the most recent version of IE.

Navigate to Settings -> Administration -> System Settings -> Customization.


Ticking this setting, will ensure that the default doc standards will be used and the heat death of the universe will have been staved off for another tick or two.

Monday, 13 January 2014

Get user's roles in MS Dynamics Crm 2011/2013

In MS Dynamics CRM 2011/13 there exists a JavaScript function to get the user's roles (Xrm.Page.context.getUserRoles) unfortunately, there are two problems with this function:
  1. It does not retrieve all roles, just those directly assigned to the user. In other words, if the user is a member of a team and that team has a role, this function will not find it.
  2. It returns the guids of the user roles rather than the names.

So in order to get around those shortcomings I wrote this function, which will return an array with all roles the user has including team roles. Note that this function is will only work in IE9+ as neither IE7 nor IE8 support indexOf, something that I will discuss in an upcoming post. It seems to work fine in Chrome (31) and Firefox (25).

function getAllUserRoles()
{
    var guid = "[A-z0-9]{8}-[A-z0-9]{4}-[A-z0-9]{4}-[A-z0-9]{4}-[A-z0-9]{12}";

    var serverUrl = Xrm.Page.context.getClientUrl();         
    var userId = Xrm.Page.context.getUserId();
    userId = userId.match(guid);
             
    var teamQuery = "TeamMembershipSet?$select=TeamId&$filter=SystemUserId eq guid'"+userId +"'";
    var teamRoleQuery = "TeamRolesSet?$select=RoleId&$filter=";
    var roleQuery = "RoleSet?$select=Name&$filter=";
    
    var teams = makeRequest(serverUrl,teamQuery,0);
    
    teamRoleQuery = composeQuery(teamRoleQuery,"TeamId",teams);
    var teamRoles = makeRequest(serverUrl,teamRoleQuery,1);

    userRoles = Xrm.Page.context.getUserRoles();
 
    if(userRoles != null){
     for(var i =0; i< userRoles.length;i++){
     teamRoles.push(userRoles[i].match(guid));
     }
    }

    roleQuery = composeQuery(roleQuery,"RoleId",teamRoles);
    var roles = makeRequest(serverUrl,roleQuery,2);
    
    return roles;
}

function makeRequest(serverUrl, query, type)
{

    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
    oDataEndpointUrl += query;

    var service = GetRequestObject();

    if (service != null)
    {

        service.open("GET", oDataEndpointUrl, false);
        service.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        service.setRequestHeader("Accept", "application/json, text/javascript, */*");
        service.send(null);

        var retrieved = $.parseJSON(service.responseText).d;

        var results =  new Array();
                               
        switch (type)
        {

        case 0:
        for (var i=0; i < retrieved.results.length;i++){
          results.push(retrieved.results[i].TeamId);
          }
          break;
        case 1:
        for (var i=0; i < retrieved.results.length;i++){
          results.push(retrieved.results[i].RoleId);
          }
          break;

        case 2:
        for (var i=0; i < retrieved.results.length;i++){
          if(results.indexOf(retrieved.results[i].Name)==-1){
           results.push(retrieved.results[i].Name);
           }
          }
          break;
         }                                     
        return results;
     }
return null;
}

function GetRequestObject()
{

    if (window.XMLHttpRequest)
    {
        return new window.XMLHttpRequest;
    }
    else
    {
        try
        {
            return new ActiveXObject("MSXML2.XMLHTTP.3.0");
        }
        catch (ex)
        {
            return null;
        }
    }
}

function composeQuery (queryBase, attribute, items )
{
 if (items != null){
     for(var i=0; i < items.length; i++){
      if (i==0)
      {
       queryBase += attribute + " eq (guid'"+items[i]+"')";
      }
      else
      {
       queryBase += " or " + attribute + " eq (guid'"+items[i]+"')";
      }
     }
    }
 return queryBase;
}

 
I think one of these days I will get the hang of JavaScript but I still find it really obnoxious to work with.

Monday, 6 January 2014

Permissions issues affecting Views in Ms Dynamics CRM 2011

A few weeks back we had an interesting issue come up.

We have a custom entity called centre with a 1:N to another custom entity called transaction (wen_trans) [transaction has a relationship with experience (wen_exp)] and in the centre record we display the related transactions. Except that in this case it wasn't showing anything and there was an interesting error message for a particular set of users:



To make matters interesting, using Advanced Find revealed all the (transaction) records that should have been displayed in the associated view and not only that, but the users were able to open the records, so I can't have been permissions right?

This is the error message in the crm logs:
Principal user (Id=666dd230-5b57-e311-8eab-0050568e2c11, type=8) is missing prvReadwen_exp privilege (Id=1a23a4a4-6f81-4410-9df8-d6a7a052ae72), ErrorCode: -2147220960, InnerException: Microsoft.Crm.CrmSecurityException: Principal user (Id=666dd230-5b57-e311-8eab-0050568e2c11, type=8) is missing prvReadwen_exp privilege (Id=1a23a4a4-6f81-4410-9df8-d6a7a052ae72)
· MessageProcessor fail to process message 'RetrieveMultiple' for 'wen_trans'.
So we modified the roles temporary giving read access to experience and the issue was sorted but why was the issue occurring in the first place?

Well, it turns out that the trace log was right all along and we needed read access to the experience entity as the associated view for transaction had some columns that were displaying data from the experience entity.

This is one of those issues that are very obvious once you know the answer but pretty tricky to find said answer, so hope it helps.