Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Monday, 21 September 2015

ID1073: A CryptographicException occurred when attempting to decrypt the cookie using the ProtectedData API (see inner exception for details).

A few weeks back, the performance testers found this issue when doing some resilience testing:
ID1073: A CryptographicException occurred when attempting to decrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false.
In essence, if the user started his session on server 1, then that server was stopped, when he connected to server 2, the issue would occur. Annoyingly, this would require removing cookies, which our testers are not able to do, on their locked down machines.

The deployment uses an NLB flag was checked for this deployment, but this seemed to make no difference, so we decided to encrypt the cookies using a certificate rather than the machine key.

This is a bit annoying as there are few things to consider:
  1. We're going to have to modify the MS Dynamics CRM web.config, which means that every new patch, update, etc.. might overwrite it.
  2. Following from that we need a deployment script to automate it as much as possible.
  3. We'll need to store a way to id the certificate to be used for the encryption somewhere easily accessible (We could hard code it but then when the certificate expires we'd been in trouble).
I decided to use the registry for 3.

This is the code that we've used to encrypt the cookies with a certificate.

using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Web;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace CRM.RSASessionCookie
{
    /// <summary>
    /// This class encrypts the session security token using the RSA key
    /// of the relying party's service certificate.
    /// </summary>
    public class RsaEncryptedSessionSecurityTokenHandler : SessionSecurityTokenHandler
    {
        static List<CookieTransform> transforms;

        static RsaEncryptedSessionSecurityTokenHandler()
        {            
            string certThumbprint = (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM",
                "CookieCertificateThumbprint",
                null);

            if (!string.IsNullOrEmpty(certThumbprint))
            {
                X509Certificate2 serviceCertificate = CertificateUtil.GetCertificate(StoreName.My,
                                                         StoreLocation.LocalMachine, certThumbprint);

                if (serviceCertificate == null)
                {
                    throw new ApplicationException(string.Format("No certificate was found with thumbprint: {0}", certThumbprint));
                }

                transforms = new List<CookieTransform>() 
                         { 
                             new DeflateCookieTransform(), 
                             new RsaEncryptionCookieTransform(serviceCertificate),
                             new RsaSignatureCookieTransform(serviceCertificate),
                         };
            }
            else
            {
                throw new ApplicationException(
                @"Could not read Registry Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM\CookieCertificateThumbprint.\nPlease ensure that the key exists and that you have permission to read it.");
            }
        }

        public RsaEncryptedSessionSecurityTokenHandler()
            : base(transforms.AsReadOnly())
        {
        }
    }

    /// <summary>
    /// A utility class which helps to retrieve an x509 certificate
    /// </summary>
    public class CertificateUtil
    {
        /// <summary>
        /// Gets an X.509 certificate given the name, store location and the subject distinguished name of the X.509 certificate.
        /// </summary>
        /// <param name="name">Specifies the name of the X.509 certificate to open.</param>
        /// <param name="location">Specifies the location of the X.509 certificate store.</param>
        /// <param name="thumbprint">Subject distinguished name of the certificate to return.</param>
        /// <returns>The specific X.509 certificate.</returns>
        public static X509Certificate2 GetCertificate(StoreName name, StoreLocation location, string thumbprint)
        {
            X509Store store = null;
            X509Certificate2Collection certificates = null;           
            X509Certificate2 result = null;

            try
            {
                store = new X509Store(name, location);
                store.Open(OpenFlags.ReadOnly);
                //
                // Every time we call store.Certificates property, a new collection will be returned.
                //
                certificates = store.Certificates;

                for (int i = 0; i < certificates.Count; i++)
                {
                    X509Certificate2 cert = certificates[i];

                    if (cert.Thumbprint.Equals(thumbprint, StringComparison.InvariantCultureIgnoreCase))
                    {
                        result = new X509Certificate2(cert);
                        break;
                    }
                }                
            }
            catch (Exception ex)
            {
                throw new ApplicationException(string.Format("An issue occurred opening cert store: {0}\\{1}. Exception:{2}.", name, location, ex));
            }
            finally
            {
                if (certificates != null)
                {
                    for (int i = 0; i < certificates.Count; i++)
                    {
                        X509Certificate2 cert = certificates[i];
                        cert.Reset();
                    }
                }

                if (store != null)
                {
                    store.Close();
                }
            }

            return result;
        }
    }
}

Company standards dictate that this class should be deployed to GAC but it can be deployed to the CRM webpage bin folder instead.

This is the PowerShell function in our script that sets the certificate on the registry:

function SetCookieCertificateThumbprint
{
 param ([string]$value)
 $path = "hklm:\Software\microsoft\mscrm"
 $name = "CookieCertificateThumbprint"
 
 if( -not (Test-Path -Path $path -PathType Container) )
 {
  Write-Error "Cannot find MSCRM Registry Key: " + $path
 }
 else
 {
  $keys = Get-ItemProperty -Path $path

  if ($keys.$name -or $keys.$name -ne $value)
  {
   Set-ItemProperty -path $path -name $name -value $value 
  }
 }
}

I have not automated the rest, which is the really fiddly part, i.e. updating the web.config. Here are the relevant parts though:

Config Sections First:
<configSections>
    <!-- COMMENT:START CRM Titan 28973
   If you add any new section here , please ensure that section name is removed from help/web.config
End COMMENT:END-->
    <section name="crm.authentication" type="Microsoft.Crm.Authentication.AuthenticationSettingsConfigurationSectionHandler, Microsoft.Crm.Authentication, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
The actual token handler:
<microsoft.identityModel>
  <service>   
    <securityTokenHandlers>
      <!-- Remove and replace the default SessionSecurityTokenHandler with your own -->
      <remove type="Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add type="CRM.RSASessionCookie.RsaEncryptedSessionSecurityTokenHandler, CRM.RSASessionCookie, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d10ca3d28ba8fa6e" />
    </securityTokenHandlers>
  </service>
</microsoft.identityModel>

It should, hopefully be obvious that this will need to be done across all CRM servers that have the Web Server role.

After all this work, we thought we would be ok for our dev and test environments sharing a single ADFS server, as the cookies would be encrypted with the same certificate, but it turns out that this is not supported by MS Dynamics CRM 2013 :(

Monday, 24 August 2015

Understanding default values in C#

I was refactoring a bit of code last week and I ended creating an interesting scenario.
In short, I was re-writing a method to see if by using a completely different approach I could make the code simpler easier to maintain and faster, and I ended up with two methods like this (don't even ask, what I was thinking):
public class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }

    public void Move(int distance)
    {
        this.X += distance;
        this.Y += distance;
    }

    public bool Move(int distance, bool dummy = true)
    {
        this.X += distance;
        this.Y += distance;
  
        return true;
    }
}
So with this calling code, which polymorphic operation will be selected:
class Program
{
    static void Main(string[] args)
    {
        var p = new Point(1, 2);
        p.Move(1);
        p.Move(1,true);
    }
}

p.Move(1) invokes the void method p.Move(1,true); invokes the bool method.

The question is how does the compiler know which polymorphic operation to invoke, after all, if the Point class had no void Move method, both statement would invoke the second Move method. 

Time to look at the IL output of Main:


.method private hidebysig static 
 void Main (
  string[] args
 ) cil managed 
{
 // Method begins at RVA 0x2100
 // Code size 27 (0x1b)
 .maxstack 3
 .entrypoint
 .locals init (
  [0] class ConsoleApplication6.Point p
 )

 IL_0000: nop
 IL_0001: ldc.i4.1
 IL_0002: ldc.i4.2
 IL_0003: newobj instance void ConsoleApplication6.Point::.ctor(int32, int32)
 IL_0008: stloc.0
 IL_0009: ldloc.0
 IL_000a: ldc.i4.1
 IL_000b: callvirt instance void ConsoleApplication6.Point::Move(int32)
 IL_0010: nop
 IL_0011: ldloc.0
 IL_0012: ldc.i4.1
 IL_0013: ldc.i4.1
 IL_0014: callvirt instance bool ConsoleApplication6.Point::Move(int32, bool)
 IL_0019: pop
 IL_001a: ret
} // end of method Program::Main

This is not very illuminating, but if we comment out the void method on the Point class and get the IL output again:

.method private hidebysig static 
 void Main (
  string[] args
 ) cil managed 
{
 // Method begins at RVA 0x20e0
 // Code size 28 (0x1c)
 .maxstack 3
 .entrypoint
 .locals init (
  [0] class ConsoleApplication6.Point p
 )

 IL_0000: nop
 IL_0001: ldc.i4.1
 IL_0002: ldc.i4.2
 IL_0003: newobj instance void ConsoleApplication6.Point::.ctor(int32, int32)
 IL_0008: stloc.0
 IL_0009: ldloc.0
 IL_000a: ldc.i4.1
 IL_000b: ldc.i4.1
 IL_000c: callvirt instance bool ConsoleApplication6.Point::Move(int32, bool)
 IL_0011: pop
 IL_0012: ldloc.0
 IL_0013: ldc.i4.1
 IL_0014: ldc.i4.1
 IL_0015: callvirt instance bool ConsoleApplication6.Point::Move(int32, bool)
 IL_001a: pop
 IL_001b: ret
} // end of method Program::Main

As expected both invoke the second method but why this behaviour?

Well, it turns out that this is part of the spec:

Use of named and optional arguments affects overload resolution in the following ways:
  • A method, indexer, or constructor is a candidate for execution if each of its parameters either is optional or corresponds, by name or by position, to a single argument in the calling statement, and that argument can be converted to the type of the parameter.
  • If more than one candidate is found, overload resolution rules for preferred conversions are applied to the arguments that are explicitly specified. Omitted arguments for optional parameters are ignored.
  • If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call. This is a consequence of a general preference in overload resolution for candidates that have fewer parameters.

Saturday, 28 February 2015

Ordered Parallel Processing in C#

In one of the projects I've been working on, we need process a bunch of files containing invoice data. Processing these can be time consuming, as the files can be quite large and although the usage given to this data seems to suggest that it can be done overnight, the business has insisted in processing the files during the online day, at 17:00.

The problem is that that the files tend to contain the invoice journey through the various states and for audit purposes we need to process them all.

So, for instance if the first record on a file is an on hold invoice, we want to process this, but we also want to process the same invoice showing as paid further down the file. We can't just process the paid event. Furthermore, we also want the invoice record to end with a status of paid, which is fairly reasonable.

The problem is that if we process the invoices in parallel, we have no guarantees that they will be processed in the right order, so a paid invoice record might end up with a state of issued, which is not great, so we just went for the quick and easy solution and thus processed the files serially.

I gave the matter a little bit more thought and came up with this:

private void UpdateInvoices(IEnumerable<IInvoice> invoices)
{
    var groupedInvoices = invoices.GroupBy(x => x.Status)
        .OrderBy(x => x.Key)
        .Select(y => y.Select(x => x));

    foreach (var invoiceGroup in groupedInvoices)
    {
        Parallel.ForEach(invoiceGroup, po, (invoice) =>
        {
           UpdateInvoice(invoice);
        });
    }
}
What we do is, we group all the invoices by status and order them by status. We then process all of the invoices in a status group in parallel, so that all invoices with status issued, get processed first, and paid last, a few more get processed in between.

It is, of course, possible to have multiple parallel for each loops for each status, but I feel that this solution is more elegant and easier to maintain.

PLinq does have an AsOrdered method, but the UpdateInvoice method doesn't return anything, if it fails to update the database, it simple logs it and it's for the server boys and girls to worry about.

Furthermore, it simply doesn't quite work as I might have expected it to work.

The code from this sample has been modified to better simulate what we're trying to achieve:

var source = Enumerable.Range(9, 50);

var parallelQuery = source.AsParallel().AsOrdered()
    .Where(x => x % 3 == 0)
    .Select(x => { System.Diagnostics.Debug.WriteLine("{0} ", x); return x; });

// Use foreach to preserve order at execution time. 
foreach (var v in parallelQuery)
{
    System.Diagnostics.Debug.WriteLine("Project");
    break;
}

// Some operators expect an ordered source sequence. 
var source = Enumerable.Range(9, 30);

var parallelQuery = source.AsParallel().AsOrdered()
    .Where(x => x % 3 == 0)
    .Select(x => { System.Diagnostics.Debug.WriteLine("{0} ", x); return x; });

// Use foreach to preserve order at execution time. 
foreach (var v in parallelQuery)
{
    System.Diagnostics.Debug.WriteLine("Project");
    break;
}

// Some operators expect an ordered source sequence. 
var lowValues = parallelQuery.Take(10);

int counter = 0;
foreach (var v in lowValues)
{
    System.Diagnostics.Debug.WriteLine("{0}-{1}", counter, v);
    counter++;
}
The call to Debug.WriteLine is the same as UpdateInvoice in the code above, in the sense that they are both void methods that cause side effects.

This is what the above prints:
9 15 18 12 30 33 36 21 24 27
Project
9 15 18 12 30 21 36 27 24 33 
0-9 1-12 2-15 3-18 4-21 5-24 6-27 7-30 8-33 9-36 


As you can see the end result is ordered but the getting there isn't, and the getting there is what we're interested in, which is why we could not use PLinq.


Saturday, 17 January 2015

String vs string in C#

What's the difference between String and string in C#?

I don't recall why I started asking this question at interviews, I don't actually think it tells me anything other than whether the interviewee knows the difference between string and String and now I find that I can't stop myself from asking it.

I just want somebody to give me the right answer, one person, just one person would do.

Thus far, I've asked this question at eight interviews, yes, I know a pitifully small sample size, but still I would've expected to have found somebody who was aware of the difference or lack thereof. 

It's not like all interviewees have been fresh out of uni, in fact only one has been.

By far the most common answer (~90%) is :

String is class

For those of you wondering, what the difference is: There is none

What a bastard, right?

string is just an alias of System.String.

Monday, 15 December 2014

OrganizationServiceContext performance in MS Dynamic CRM 2013

The OrganizationServiceContext has a SaveChanges method that essentially does what it says on tin, namely the changes on the objects back to the database.

I've never used this method, preferring to use the regular Update method  on the IOrganizationService instead, but last week I had a moment of doubt, what if it is faster, what if it's multi-threaded, so I decided to run some tests to see whether there was any performance difference, spoiler alert: There wasn't
The tests consisted of the update of a three fields on the account entity for 2000 accounts and were run from an idle CRM server to minimize the influence of network traffic.

Results shown below are for the average of the three runs I did, except for the parallel version, using Parallel.ForEach, where it shows the single run I did.

OrganizationServiceContext IOrganizationService IOrganizationService (Parallel)
126.7 s 130.1 s 49.6 s

My fears were unfounded and the SaveChanges method, while slightly faster in these tests, does not seem to be appreciatively faster.

The difference is less than 3% and the slowest run using the OrganizationServiceContext was basically the same as the fastest with IOrganizationService : ~129 s


Friday, 21 November 2014

TIL - Solving Out of Memory exceptions in .NET

So yesterday I was playing about with a high memory VM in Azure and I wrote a little app to swallow the server's RAM whole.
Only thing is that it stopped running and didn't swallow the server's RAM whole.

So I changed the build to x64, but no dice.

After a bit of googling, it turn out that by default there is a limit that needs to be defeated, the 2 GB limit and it can be defeated by adding the following to the config file.
<configuration>
 <runtime>
  <gcAllowVeryLargeObjects enabled="true" />
 </runtime>
</configuration>

Tuesday, 30 September 2014

TIL - Format Guids to string in C#

Too long to explain, oh yes, it was SharePoint related, but I learnt today about various options for formatting Guids available in the framework:

Shameless copy and paste from this page.

Specifier
Format of return value
N
32 digits:
00000000000000000000000000000000
D
32 digits separated by hyphens:
00000000-0000-0000-0000-000000000000
B
32 digits separated by hyphens, enclosed in braces:
{00000000-0000-0000-0000-000000000000}
P
32 digits separated by hyphens, enclosed in parentheses:
(00000000-0000-0000-0000-000000000000)
X
Four hexadecimal values enclosed in braces, where the fourth value is a subset of eight hexadecimal values that is also enclosed in braces:
{0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}

So crmRecord.Id.ToString("N").ToUpperInvariant() results in : "7AD6FAB54528E411940D005056BC69C8"

Monday, 7 July 2014

Storing sensitive data (e.g. passwords) in MS Dynamics CRM 2011/2013

We need to integrate with a third party, who have decided that implementing a federated trust is too complex and thus have just given us a user name and password.

Since the integration is done via a couple of plug-ins and custom activities we've decided to store the password in Dynamics CRM, the only problem is that there is no out of the box way of storing the passwords that would allow a relatively simple automated deployment.

Sure, we can register plug-in and use the secure configuration but that means deploying the solution and then re-registering the plug-ins with the secure data but this wasn't suitable.

We ruled out symmetric encryption as we would just have the same problem but for the encryption key, so the obvious choice was asymmetric encryption, the problem is that asymmetric encryption is not really suitable for large amounts of data, so we settled on the recommend way of using asymmetric encryption to encrypt the encryption key of a symmetric encryption scheme.

The thing is, the .NET framework sort of includes this in the form of the EncryptedXml class, which can use a certificate to encrypt and decrypt Xml documents and we can use this for storing a password for instance.

A sample of how to use this class can be seen below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using log4net;

namespace Encryption
{
    public class Encryption
    {
        ILog logger;
        X509Certificate2 certificate;

        public Encryption(string certificateThumbprint) 
        {
            logger = LogManager.GetLogger("Encryption");
            certificate = GetCert(certificateThumbprint);
        }

        public string Encrypt(string plaintext)
        {
            XmlDocument Doc = new XmlDocument();
            Doc.LoadXml(string.Format("<sensitivedata>{0}</sensitivedata>", HttpUtility.HtmlEncode(plaintext)));
            Doc.PreserveWhitespace = true;
            XmlElement toEncrypt = Doc.GetElementsByTagName("sensitivedata")[0] as XmlElement;
            EncryptedXml eXml = new EncryptedXml();
            EncryptedData edElement = eXml.Encrypt(toEncrypt, certificate);
            EncryptedXml.ReplaceElement(toEncrypt, edElement, false);
            return Doc.OuterXml;
        }

        public string Decrypt(string encryptedtext)
        {
            XmlDocument Doc = new XmlDocument();
            Doc.LoadXml(encryptedtext);
            EncryptedXml exml = new EncryptedXml(Doc);
            exml.DecryptDocument();
            string plaintext = HttpUtility.HtmlDecode(XDocument.Parse(Doc.OuterXml).Element("sensitivedata").Value);
            return plaintext;
        }

        private X509Certificate2 GetCert(string thumbprint)
        {
            X509Certificate2 cert = null;
            X509Store store = new X509Store(StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            try
            {
                X509Certificate2Collection certCollection = store.Certificates;
                cert = certCollection.Cast<X509Certificate2>().Where(c => c.Thumbprint.Equals(thumbprint)).Single();
            }
            catch (Exception ex)
            {
                logger.ErrorFormat("An error occurred looking for certificate with thumbprint: {0}.\nException:{1}."
                    , thumbprint, ex);
            }
            finally
            {
                store.Close();
            }
   
            return cert;
        }
    }
}

The thing to note is that sensitive data to be encrypted needs to be valid xml data, in other words, if your sensitive data contains ampersands it will not be parsed, which is why I use the HttpUtility class to encode and decode the sensitive data.
 
 It does seem a little bit fiddly but it is quite almost all done by the framework, so it's really less code to maintain and the extra text that needs to be stored is not a consideration as it's a single record.
 
Furthermore, it would be trivial to modify and return a plaintext Xml Document for processing instead of the value of an element, but the value of an element is what I needed.

Monday, 23 June 2014

Use Linq Joins (Method Syntax) with Late Binding in Ms Dynamics CRM 2011/2013

There seems to be no examples of using Linq with late binding and method syntax about, until now:

var collection = context.CreateQuery("service")
.Join(context.CreateQuery("e2_dictionary"),x => x["serviceid"], y => (y["e2_serviceid"as EntityReference).Id, (x, y) => y)
.Where(x => x["name"].Equals(serviceName));
The out of the box service entity is pretty locked down, it's not possible to add N:1 or N:N relationships, so in order to implement a classification system for the services, I created the e2_dictionary entity.

e2_dictionary has a N:1 with e2_type and service, which means that there is some validation needed in a plug-in to ensure that when that nobody creates more than one e2_dictionary and this is why the snippet above, serviceName is the name of the service :)
 
 


Monday, 24 February 2014

Boxing issues in C# - Brain Dump 1

I'm going through my emails collecting all the interesting stuff that I have accrued over the past year or so, most of it not mine and dumping it here in various posts, I could just keep the pst file but ....

       [TestMethod]
       public void TestMethod1()
        {
            decimal d1 = 20;
            decimal d2 = 20;

            Assert.IsTrue(compare (d1, d2));
        } 

        private bool compare (object o1, object o2)
        {
            return o1 == o2;
        }

The test above will fail, should use this instead:

        private bool compare (object o1, object o2)
        {
            return o1.Equals(o2);
        }

Thursday, 27 June 2013

Domain Issues with NetworkCredential class in C#

On Friday we had some interesting issues related to the NetworkCredential class.

We were implementing some functionality that was very similar to an already existing piece of functionality, in essence we were calling a third party web service that required authentication. In reality is not a third party as we have the code for it, but it's for a different application, so for all intents and purposes we treat it as a third part service, i.e. it's a black box.

Since we had some unit tests for this call, we ran through them and we found an issue, it would not authenticate to the web service, so the test would fail. 

We checked through the application and the web service was working fine, which should have led us to believe that there was something wrong with the unit tests, but we assumed that the unit tests were working before and it was something environmental that was causing the issue.

As it turns out somebody had changed the unit tests and not bothered to test them. The issue was the following:
var cred=new NetworkCredential(@"dev\testuser", "ReallySecurePass1"); 
instead of:
var cred=new NetworkCredential("testuser", "ReallySecurePass1","dev");

Friday, 17 May 2013

MS Dynamics CRM 2011 Workflows not sending emails - Set Allow other Microsoft Dynamics CRM users to send E-mail on your behalf privilege programmatically

Today I had an issue where a workflow would fail to complete, oddly it was failing on a Send Email Activity, with the following error message:
You cannot send e-mail as the selected user. The selected user has not allowed this or you do not have sufficient privileges to do so.Contact your system administrator for assistance.
The issue is caused by users not having send as privileges set. Note that this will only occur if the sender is different from the owner of the email, which will be the case if you have workflows owned by the service account sending emails on user's behalf, a common enough business scenario.

Here's the method I used to allow this:

private void SetSendAsSetting(IOrganizationService service, Entity user, bool state)
{
    try
    {               
        UpdateUserSettingsSystemUserRequest updateRequest = new UpdateUserSettingsSystemUserRequest();
        UpdateUserSettingsSystemUserResponse updateResponse = new UpdateUserSettingsSystemUserResponse();
        
        Entity userSettings = new Entity("usersettings");        
        
        //Ensure that help language is set
        userSettings.Attributes["helplanguageid"] = 1033;
        userSettings.Attributes["issendasallowed"] = state;
        
        updateRequest.Settings = userSettings;
        updateRequest.UserId = userId;
        
        updateResponse = (UpdateUserSettingsSystemUserResponse)service.Execute(updateRequest);
    }
    catch (Exception ex)
    {
        Logger.Write(ex);
    }
}

Sunday, 12 May 2013

Set IIS website https binding programmatically (add certificate too)

I found myself needing to set a website's https binding for a few servers, this week and I wondered whether this could be done programmatically.

Although in this case the method, see below, is used to set https as a binding it can be easily modified to add any binding type to any website. I kind of did it as halfway house, I might modify it later to make it a little bit more robust and generic.

private static void SetBinding(string siteName, string bindingInfo, string fileName,string password)
{
    using (ServerManager serverManager = new ServerManager())
    {
        Site site = serverManager.Sites.Where(x => x.Name == siteName).SingleOrDefault();

        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

        X509Certificate2 certificate = new X509Certificate2(fileName, password);

        store.Add(certificate);

        if (site.Bindings.Any(x => x.Protocol.ToLower() != binding.ToLower()))
        {
            Binding binding = site.Bindings.Add(bindingInfo, certificate.GetCertHash(), store.Name);

            binding.Protocol = "https";
        }

        store.Close();
    }
}

Note that bindingInfo should be of this form "*:443:" if you want your website to listen on all ip addresses of your server.

The certificate needs to be of PKCS #12 vintage, i.e. with a .pfx or .p12  extension (don't just change the filename extenstion to .pfx if you have a .cer or .der certificate, it won't work.)

Don't forget to add the following namespaces (the library for the Microsoft.Web.Administration namespace is called the same as the namespace).

System.Security.Cryptography.X509Certificates;
Microsoft.Web.Administration;

Tuesday, 7 May 2013

Wiring events to a Popup control in Silverlight

Last week I was doing some work on a Silverlight application and I was struggling with an event not firing for a calendar control inside a popup control and it turns out that the reason is very simple, I was wiring the event to the popup rather than the calendar control inside the popup.

This is the method I used to create the calendar within the popup. 

private void CreateCalendar()
{
    if (calendar == null)
    {
        calendar = new Calendar();
        calendar.SetValue(Grid.ColumnProperty, 10);
        calendar.SetValue(Grid.RowProperty, 0);
        calendar.SetValue(Grid.RowSpanProperty, 7);
        calendar.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
      
        var StartDateBinding = new System.Windows.Data.Binding();
        StartDateBinding.Path = new PropertyPath("StartDate");
        StartDateBinding.Mode = System.Windows.Data.BindingMode.TwoWay;
        StartDateBinding.ValidatesOnDataErrors = true;
        calendar.SetBinding(Calendar.SelectedDateProperty, StartDateBinding);

        var displayDateBinding = new System.Windows.Data.Binding();
        displayDateBinding.Path = new PropertyPath("CalendarStartsOn");
        calendar.SetBinding(Calendar.DisplayDateStartProperty, displayDateBinding);

        popup.DataContext = viewModel;

        popup.Child = calendar;            

        calendar.MouseLeave += (o, e) =>
        {
            popup.IsOpen = false;
            LayoutRoot.Children.Remove(popup);
        };
  
 calendar.SelectedDatesChanged += (o, e) =>
        {
     CreateAppointment();
            popup.IsOpen = false;
            LayoutRoot.Children.Remove(popup);
        };

    }

}

The calendar and popup objects are global objects of the MainPage partial class (i.e. they can be found in main.xaml.cs) and the CreateCalendar method gets called on the constructor, via the AddPopUp method, see below

The popup is closed when a date is selected, in which case we create an appointment, method not shown, for the user or when the mouse leaves the calendar.

private void AddPopUp()
{    
    CreateCalendar();
    LayoutRoot.Children.Remove(popup);
    LayoutRoot.Children.Add(popup);
    
    popup.VerticalOffset = 50;
    popup.HorizontalOffset = Application.Current.Host.Content.ActualWidth - 100;
    
    popup.IsOpen = true;
}

Thursday, 2 May 2013

Set Regional Settings (culture) in Silverlight application.

Today I was looking at some issues with date formats, why in god's name do the american's use a different date system than everybody else?, and I ended up using the following code to set up the Regional Settings (No prizes for guessing where I work):

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new MainPage();
 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");
 
    var root = RootVisual as  MainPage;
    if (root != null)
    {
     root.Language = XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentCulture.Name);
    }
}

This is in the App.xaml code behind file.

Note that annoyingly, this will not work in WPF.


Monday, 22 April 2013

Encoding issues with HTMLAgility pack ̢۪

I've been using the HTMLAgility pack for a while and I've never had any encoding issues before but recently I've been seen loads of encoding issues, where this ' is encoded as ’ or " is encoded as â€Å“

There is a very simple solution, instead of simply loading the document like this:

doc.Load(file.ToString());

The encoding should be specified, like this:

doc.Load(file.ToString(),System.Text.Encoding.UTF8, false);

After loading the document having specified the encoding, all the encoding issues disappeared as if by magic :)

Wednesday, 17 April 2013

Sort sitemap alphabetically according to Display name in MS Dynamics CRM 2011

A handy little class to sort a sub area of the sitemap in alphabetical order according to Display name rather than logical name.  Major Area refers to Workplace, Settings, etc.. whereas minor Area refers to the sub areas inside this, e.g. extensions.

I think it should be possible to do them in reverse alphabetical order by using the reverse() method when selecting the items, but I haven't tried. Not sure what kind of customer would want reverse alphabetical order though, as it seems counter intuitive, but there you go.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

public class SitemapSorter
{
    [ThreadStatic]
    private IOrganizationService service;

    public SitemapSorter(IOrganizationService service)
    {
        this.service = service;
    }

    public void Sort(string file, string majorArea, string minorArea)
    {
        XDocument xdoc = XDocument.Load(new StreamReader(file));

    //Get XElements in the relevant area (Workplace -> Extensions in this case)
    //Store them in a Dictionary. key is the Display name and the value is the
    //entire node (XElement).
    var nodes = xdoc.Root.Descendants("Area")
        .Where(x => x.FirstAttribute.Value == majorArea)
        .Descendants("Group")
        .Where(x => x.FirstAttribute.Value == minorArea)
        .Descendants()
        .ToDictionary(x => RetriveDisplayName(service, x.LastAttribute.Value), x => x);
              
    //Use a Sorted Dictionary, which will automatically sort on key,not on value.
    SortedDictionary<string, XElement> items = new SortedDictionary<string, XElement>(nodes);
              
    //Remove the nodes from sitemap document
    xdoc.Root.Descendants("Area")
       .Where(x => x.FirstAttribute.Value == majorArea)
       .Descendants("Group")
       .Where(x => x.FirstAttribute.Value == minorArea)
       .Descendants().Remove();
              
    //Now add them in order
    xdoc.Root.Descendants("Area")
       .Where(x => x.FirstAttribute.Value == majorArea)
       .Descendants("Group")
       .Where(x => x.FirstAttribute.Value == minorArea)
       .First().Add(items.Select(x => x.Value));

    xdoc.Save(file);
    }

    private string RetriveDisplayName(IOrganizationService service, string entityName)
    {
    string result = string.Empty;
    try
    {
        RetrieveEntityRequest request = new RetrieveEntityRequest()
        {
            EntityFilters = EntityFilters.Entity,
            LogicalName = entityName
        };
        RetrieveEntityResponse response =
            (RetrieveEntityResponse)service.Execute(request);
        result = response.EntityMetadata.DisplayName.UserLocalizedLabel.Label;
    }
    catch (Exception ex)
    {
        CommonHelper.LogException(ex);
    }
    return result;
    }
}

Friday, 12 April 2013

Use AppPool (Application Pool) credentials for Sharepoint integration

I had some fun today trying to work out how to use the AppPool Credentials for integration between our website and SharePoint.

So this is what I did.

using System.Security.Principal;

public class ImpersonateUser : IDisposable
{
  WindowsImpersonationContext ctx = null;
  WindowsImpersonationContext appPoolctx = null;

  public void ImpersonateAppPoolUser()
  {
     //RevertToSelf
     ctx = WindowsIdentity.Impersonate(IntPtr.Zero);
     // and call impersonate on the app pool user object
     appPoolctx = WindowsIdentity.GetCurrent().Impersonate();
  }

  public void UndoUserImpersonation()
  {
     if (ctx != null)
     {
        ctx.Undo();
        ctx = null;
     }

     if (appPoolctx != null)
     {
        appPoolctx.Undo();
        appPoolctx = null;
     }

  }

  public void Dispose()
  {
      UndoUserImpersonation();
  }
}

I used this class for SharePoint integration, as it seemed a quicker and simpler option than storing credential in the web.config and then encrypting.

using (ImpersonateUser ie = new ImpersonateUser())
{
    ie.ImpersonateAppPoolUser();

    using (ClientContext clientContext = new ClientContext(url))
    {
     //Get documents from SharePoint
    }
}

Monday, 18 March 2013

How to convert webpage to .mobi or .epub file

The standard advice when anybody answers this question seems to be: Use Calibre, the e-book management software. If you have a Kindle you can also use Amazon's conversion service, which is accessed by sending the webpage as an attachment to your kindle's email address (you do need to white list the email address from which you're sending the email from).

The problem is that neither of them works 100% of the time. There are online alternatives, such as instapaper, which is really handy as it allows you to combine multiple webpages into a single mobi file among other things, but I've never seen an image on any of the instapaper mobi files that I have downloaded, never.

I wrote a little console app that gets rid of the nasty stuff that makes Calibre go pop, it might work with Amazon too, but I've only tried it with Calibre as it's quicker to test.

Please bear in mind that I've only done limited testing, by the time I've done the app, done the tests and so on, I probably could have read all the "problematic" articles online, but there you go.

Also note that you will need the HtmlAgilityPack.

using HtmlAgilityPack;              
using System;              
using System.Collections.Generic;              
using System.IO;              
using System.Linq;              
using System.Text;              
using System.Threading.Tasks;              
              
namespace HTMLCleaner              
{              
 class Program              
 {              
     static void Main(string[] args)              
     {              
       if (args.Length >= 1 && args.Length <= 2)              
       {              
        try              
        {              
            string sourceFile = args[0];              
            string destFile = args.Length > 1 ? args[1] : args[0];              
                            
            HtmlDocument doc = new HtmlDocument();              
                            
            doc.Load(sourceFile);              
                            
            doc.DocumentNode.Descendants()              
                .Where(x => x.Name == "script" || x.Name == "iframe" || x.Name == "noscript").ToList()              
                .ForEach(x => x.Remove());              
                            
            using (StreamWriter sw = new StreamWriter(destFile))              
            {              
                doc.Save(sw);              
            }              
                            
            Console.WriteLine("Successfully cleaned HTML");              
        }              
        catch (Exception ex)              
        {              
            Console.WriteLine("Error: {0} - Type {1}.", ex.Message, ex.GetType());              
        }              
       }      
       else      
       {      
        Console.WriteLine("Please Invoke like this:");              
        Console.WriteLine("HTMLCleaner.exe sourcefile destinationfile");              
        Console.WriteLine("Destination file can be omitted, in which case the source file will also be the destination file");              
       }              
 
     }              
 }              
}              

Wednesday, 13 March 2013

Retrieve OptionSet Label value in MS Dynamics CRM 2011

A method I used to retrieve the label, i.e. text of an option set (I would say more but I honestly can't remember what on Earth we use it for, if we still do use it).

string GetOptionSetLabel(IOrganizationService service, string entityName, string attributeName, int attributeValue)
{
  string result = string.Empty;

  try
  {
      RetrieveAttributeRequest request = new RetrieveAttributeRequest()
      {
        EntityLogicalName = entityName,
        LogicalName = attributeName,
        RetrieveAsIfPublished = true
      };

      RetrieveAttributeResponse resp = (RetrieveAttributeResponse)service.Execute(request);

      if (resp.AttributeMetadata.AttributeType != null && resp.AttributeMetadata.AttributeType == AttributeTypeCode.Picklist)
      {

        result = (from x in ((OptionSetMetadata)((PicklistAttributeMetadata)resp.AttributeMetadata).OptionSet).Options
                  where x.Value == attributeValue
                  select x.Label.UserLocalizedLabel.Label).FirstOrDefault();
      }
      else
      {
          result = "Attribute is not picklist";
      }
  }
  catch (Exception ex)
  {
      result = string.Format("Exception of type {0} occurred: {1}", ex.GetType(), ex.Message);
  }

  return result;
}