Friday, 12 July 2013

Hosting a RESTful JSON WCF Service from a console app or a windows service.

I've been working for a while on an application, too long to explain what it actually does, but the bottom line is that it requires, or at least it could benefit from having, a RESTful WCF service hosted on both http and https endpoints.

I toyed with the idea of doing the application in Python as it uses some Python code but I decided to stick with what I knew as I wanted to finish it quickly. At any rate, here is the code:

This is simply shown as an example of how it could be done, if you follow it, your end point will be listening on http://<hostname>/store/ and can be invoked by simply navigating to it like this:

http://<hostname>/store?page=url 

In order for the application to listen on https you will need to have a valid certificate on your certificate store. The subject name should match the hostname of the machine running this application and then this should work:

https://<hostname>/store?page=url 

First the interface:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfJsonRestService
{
    [ServiceContract]
    public interface IStore
    {
        [OperationContract]
        bool Store(string item);
    }

}
Then the class implementing the interface:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Configuration;

namespace WcfJsonRestService
{
   
    public class Store : IStore
    {
        [WebInvoke(Method = "GET",
                    ResponseFormat = WebMessageFormat.Json,
                    UriTemplate = "store?page={item}")]
        public bool Store(string item)
        {
            //do stuff here

            return true;
        }

    }
}
And finally a Console application that hosts the service.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;

namespace WcfJsonRestService
{
    class Program
    {
        static void Main(string[] args)
        {

            try
            {
                using (ServiceHost host = new ServiceHost(typeof(RESTful)))
                {

                    AddServiceEndPoint(host, "https://{0}/store", true, "change me");
                    AddServiceEndPoint(host, "http://{0}/store", false);

                    host.Open();

                    Console.WriteLine("Service host running......");
                    Console.WriteLine("Press Any key at any time to exit...");

                    Console.Read();

                    host.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                Console.Read();
            }


        }

        private static void AddServiceEndPoint(ServiceHost host, string url, bool useSSLTLS, string certSubjectName="")
        {
            string addressHttp = String.Format(url,
                System.Net.Dns.GetHostEntry("").HostName);


            WebHttpBinding binding;

            if (useSSLTLS)
            {

                binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
                binding.HostNameComparisonMode = HostNameComparisonMode.WeakWildcard;
                binding.CrossDomainScriptAccessEnabled = true;
            }
            else
            {
                binding = new WebHttpBinding(WebHttpSecurityMode.None);
                binding.CrossDomainScriptAccessEnabled = true;
            }

            // You must create an array of URI objects to have a base address.
            Uri uri = new Uri(addressHttp);
            Uri[] baseAddresses = new Uri[] { uri };

            WebHttpBehavior behaviour = new WebHttpBehavior();
            // Add an endpoint to the service. Insert the thumbprint of an X.509 
            // certificate found on your computer. 
            host.AddServiceEndpoint(typeof(IRESTful), binding, uri).EndpointBehaviors.Add(behaviour);

            if (useSSLTLS)
            {
                host.Credentials.ServiceCertificate.SetCertificate(
                    StoreLocation.LocalMachine,
                    StoreName.My,
                    X509FindType.FindBySubjectName,
                    certSubjectName);
            }
        }
    }
}

Alternatively, the WCF service can be hosted by a Windows service. Code behind for windows service here:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using StoreAndConvert.WCFService;
using System.Security.Cryptography.X509Certificates;
using System.Configuration;


namespace StoreAndConvert.WindowsService
{
    public partial class Store : ServiceBase
    {

        string certSubjectName = string.Empty;

        ServiceHost host;

        public Store()
        {
            InitializeComponent();
        }


        protected override void OnStart(string[] args)
        {
            try
            {
                //Debugger.Launch();
                certSubjectName = ConfigurationManager.AppSettings["CertificateSubjectName"];

                host = new ServiceHost(typeof(StoreUrls));

                AddServiceEndPoint(host, "https://{0}/storeurl", true, certSubjectName);
                AddServiceEndPoint(host, "http://{0}/storeurl", false);

                host.Open();

                Trace.WriteLine("Service host running......");
                Trace.WriteLine("Listening on");

                foreach (ServiceEndpoint sep in host.Description.Endpoints)
                {
                    Trace.WriteLine(string.Format("endpoint: {0} - BindingType: {1}",
                        sep.Address, sep.Binding.Name));
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }

        }

        protected override void OnStop()
        {
            try
            {
                if (host != null)
                {
                    host.Close();
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
        }

        private void AddServiceEndPoint(ServiceHost host, string url, bool useSSLTLS, string certSubjectName = "")
        {
            string addressHttp = String.Format(url,
                System.Net.Dns.GetHostEntry("").HostName);

            WebHttpBinding binding;

            if (useSSLTLS)
            {
                binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
                binding.HostNameComparisonMode = HostNameComparisonMode.WeakWildcard;
                binding.CrossDomainScriptAccessEnabled = true;
            }
            else
            {
                binding = new WebHttpBinding(WebHttpSecurityMode.None);
                binding.CrossDomainScriptAccessEnabled = true;
            }

            // You must create an array of URI objects to have a base address.
            Uri uri = new Uri(addressHttp);
            Uri[] baseAddresses = new Uri[] { uri };

            WebHttpBehavior behaviour = new WebHttpBehavior();
            // Add an endpoint to the service. Insert the thumbprint of an X.509 
            // certificate found on your computer. 
            host.AddServiceEndpoint(typeof(IStoreUrls), binding, uri).EndpointBehaviors.Add(behaviour);

            if (useSSLTLS)
            {
                host.Credentials.ServiceCertificate.SetCertificate(
                    StoreLocation.LocalMachine,
                    StoreName.My,
                    X509FindType.FindBySubjectName,
                    certSubjectName);
            }
        }
    }
}

Wednesday, 10 July 2013

Run Wix installer using elevated permissions

In my last post I talked about setting a certificate binding for an IIS website from a Wix installer, which required elevated permissions in order for the operation to work.

The solution involved checking that the user was running using elevated permissions, which was simple enough but it turns out there is a far neater solution to achieve this:

 <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" InstallPrivileges="elevated" />

Sunday, 7 July 2013

Assign Certificate (Set HTTPS Binding certificate) to IIS website from Wix Installer

I'm working on this project where we have a secure website and I was tasked with creating an installer for it. After quite a few searches and not coming up with any results I went down the Custom Action route.

Not shown here is how to install the website for which we are modifying the binding.

This is very simple, it just uses IIS server manager to set the binding for the certificate, note that since this operation requires elevation of permissions, there is a check to ensure that the user is running with elevated permissions, if this is not the case then an the NotElevated custom action will be triggered, and error messaged displayed and the installation will be rolled back.

This is the Custom Action code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Web.Administration;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
using System.Security.Principal;

namespace Installer.CustomActions
{
    public class CustomActions
    {
        const string protocol = "https";
        const string bindingPattern = "*:{0}:";


        [CustomAction]
        public static ActionResult UpdateBinding(Session session)
        { 
            ActionResult result = ActionResult.Failure;
            session.Log("Start UpdateBinding.");
   if (CheckRunAsAdministrator())
   {
    bool outcome=       UpdateBinding("Portal", protocol, string.Format(bindingPattern, session["SSLPORT"]), session["CERT"], session);
     if(outcome){result = ActionResult.Success;}
                            session.Log("End UpdateBinding.");
                            return result;
   }
   else
   {
       session.Log("Not running with elevated permissions.STOP");
              session.DoAction("NotElevated");
   }
        }

        private static bool UpdateBinding(string sitename, string protocol, string port, string certSubject, Session session)
        {
            bool result=false; 
            session.Log(string.Format("Binding info (Port) {0}.", port));
            session.Log(string.Format("Certificate Subject {0}.", certSubject));

            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);

                var certificate = store.Certificates.OfType<X509Certificate2>().Where(x => x.Subject == certSubject).FirstOrDefault();

                if (certificate != null)
                {
                    session.Log(string.Format("Certificate - Friendly Name: {0}. Thumbprint {1}", certificate.FriendlyName, certificate.Thumbprint));

                    site.Bindings[0].CertificateHash = certificate.GetCertHash();
                    site.Bindings[0].CertificateStoreName = store.Name;
                    site.Bindings[0].BindingInformation = port;

                    serverManager.CommitChanges();
                    result=true;
                }

                session.Log(string.Format("Could not find a certificate with Subject Name:{0}.", certSubject));

                store.Close();

            }
            return result;    
        }

        /// <summary>
        /// Check that process is being run as an administrator
        /// </summary>
        /// <returns></returns>
        private static bool CheckRunAsAdministrator()
        {
            var identity = WindowsIdentity.GetCurrent();
            var principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }
    }
}
and here is the Wix markup that uses the above custom action :
<Product ....>
<!--All the rest of the stuff-->

    <Binary Id="CA" SourceFile="$(var.Installer.CustomActions.TargetDir)Installer.CustomActions.CA.dll"/>

    <CustomAction Id="UpdateBinding" BinaryKey="CA" DllEntry="UpdateBinding" Execute="immediate" Return="check" />

    <CustomAction Id="NotElevated" Error="Ensure that the Installer is Run with elevated permissions (i.e. Run as Administrator)" />

    <InstallExecuteSequence>
      <Custom Action="UpdateBinding" After="InstallFinalize">NOT Installed</Custom>
    </InstallExecuteSequence>
</Product>

Tuesday, 2 July 2013

Assert.AreEqual() failing for strings that are equal

Today I almost lost it while doing the simplest of unit tests.

In essence, we had a plugin that would fire on an entity being updated and would set the name of a custom entity to a particular string retrieved from a remote service. The thing was that even though the strings were the same, Assert.AreEqual() was failing.

After many attempts with various StringComparison options, using Trim in a fit of desperation I created a method to check each character in the actual string against the expected string and lo and behold they were different.

The actual string, coming from CRM was using character 160, which is a non-breaking space while the C# code was using character 32, which is a simple space.

The solution was to replace the character 160 with character 32, now the unit tests pass.

Code:

const string FirstName="A Random Name";

[TestMethod]
public void CheckRuleName()
{
    Entity entity = Service.Retrieve("H2H_rule", RuleId, new Microsoft.Xrm.Sdk.Query.ColumnSet("H2H_name"));
    entity = UpdateEntity(entity, RuleId);
 
    string result = entity.Attributes["H2H_name"].ToString().Replace((char)160,(char)32);
 
    Assert.AreEqual(FirstName, result);
}

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");

Saturday, 22 June 2013

News is bad for you – and giving up reading it will make you happier

A few months back I read this article about how News is bad for you and I thought I would give the advise a try to see what would happen. I would give up the News, sort of. Would it result in any kind of improvement in my day to day? Would it make me more .. or less ..?

The first thing to point out is that I haven't completely cut myself out of the news cycle, my radio alarm is still set at 07:00 every morning, with the (electronic) dial set to Radio 4, so I do get some news, not many as I'm normally out of the door by the time the sports section start, so this is normally about 25 minutes or fewer.

Furthermore, I don't actually go out of my way to avoid the news, but since I don't own a television, this means that at most, I'm only going to catch the odd glimpse of the news from a TV left on somewhere or a newspaper laying about. In essence, what I have done is stopped browsing news websites, mostly the BBC and various other newspapers here and abroad. 

So, Gave I had an epiphany? Has my (mild) depression lifted? my productivity increased 2 fold? 3 fold? 10 fold? Has anything changed at all?

Perhaps, unsurprisingly, there is very little that has changed in my life. I think this is probably down to two main facts:

  1. Rolf Dobelli is mostly right in his assertion that we don't really need the news.
  2. I avoid local news like the plague.
I admit that I have not completely cut myself off from the News, so perhaps we do need the News, but he is correct that there is hardly a news item that, has affected my life in any meaningful way, and I have benefited from knowing it as early as possible or most of them at all.

It is worthwhile mentioning black swans events and opportunity cost here. The former, as it would seem that only such events would be worthwhile knowing about as soon as possible and opportunity cost because all the time, mainly, spent consuming the news in the vain hope of being ready for the black swan event, which might never come and even if it does, will it compensate for all the other things that could have been done with that time (money)?

On the aftermath of the Boston bombing, somebody wrote a blog post/article on the best way of having the most accurate information about the bombing or any such event. Their suggestion was to turn yourself off from the electronic world, go out with your mates to the park or something and then read all about it on the morning paper. 

Mr Dobelli would probably argue that even reading about on the morning paper would be a waste of time, which is probably true.

I think number 2 is the key to why I feel very little change in my life without a constant stream of news.

There really is no easy way of saying this, but Local news are simply evil. They tend to concentrate, overwhelmingly, on crime and because by their very own definition, they are local, it does not even allow us to dismiss them as something that would/could not happen here, as it has happened. Furthermore, since we are notoriously bad at probability, reading them is very likely to make us anxious even though nothing, or very little, has changed about the probability of being the victim of a crime. Yet, reading about it is likely to have made us more likely to believe that crime is worse than what it actually is and even, and I'm going on a limb here, before we read the story about the horrific crime.

The murder rate in the UK is 1.2 per 100000 inhabitants or 12 per million inhabitants. The local news in my area cover approximately 1000000 people (some local media cover smaller areas, of course), which means that on average there will be 12 murders a year, or put another way: 1 per month. Not enough to be a constant worry, but enough to be a constant reminder. Never mind the fact that most of the crime is essentially criminals killing each other. Yes, there are cases were there are random acts on innocent people, but just because you can easily recall an example does not mean it's common, in fact, it's quite the opposite. Media coverage tends to be inversely proportional to frequency of an event. This is one of the reasons why the attacks on the London bombings on July 7th 2005, got the coverage they got. 

The one positive effect, that this voluntary withdrawal from news sites has had in my life, is related to my somewhat complicated relationship with sports, which I'm not going to go into detail here, but suffice to say that not knowing anything sport news has left me without those little moments of joy when the results went my way or those loooong periods of annoyance, frustration, irritation and helplessness when they didn't (I am of course exaggerating a little here for effect).

If you consume local news, I do recommend that you stop, for everybody else you can probably carry on as you were, but know that being au fait with the latest is unlikely to be of much use for anything unless your job depends on it, in which case what the hell are you doing reading this blog?

Monday, 17 June 2013

Interesting behaviour of MS Dynamics CRM 2011 when updating fields.

I found out something interesting, although, really annoying is more accurate today about updating fields in MS Dynamics CRM 2011 today.

Updating an entity field via custom code (either plugin, custom workflow activity or a simple app) or through a workflow activity, Update Record, will always trigger an Update event, even if the field is not actually being changed, i.e. it's being updated with the same value.

This might sound obvious, but it wasn't to me and some of my colleagues and it has repercussions for workflows that trigger on record field changes as they will be triggered when the value is set, regardless of whether it was an actual update or not.

This is slightly disappointing as this can lead to workflows being triggered despite the fact that for all intents and purposes, the field has not actually been changed and yet the workflow will be triggering, which could lead to users raising issues because things are happening in the system that shouldn't be happening.

The obvious answer is not to do the update if the value is not changing, which is what we are doing in a few places were this is an issue, but it seems to me that the platform, i.e. MS Dynamics CRM 2011, should be handling this. Particularly given that it's possible to prevent users from seeing the audit information, if auditing is indeed on at all, which for many things, it could well not be on.

Oh Well. 

Wednesday, 12 June 2013

Developers vs Testers

Last week I had the usual argument between developers and testers. (Note, I did not actually write the code that was the source of the issue, but I am getting ahead of myself.)

Tester: Why did you close the ticket as working to spec?
Me: Occam's razor. 
Tester looks baffled at me
Me: Wikipedia
Tester looks even more baffled
Me: Look it up in Wikipedia
Tester left admitting defeat.

That's how it should've played out but alas it wasn't.

The issue raised was that the invoices related to an account did not have the correct number, for various reasons too involved to explain here, we have separate auto numbering counters for different invoice types, thus it's perfectly valid to have 30 invoices and the 31st be numbered 1 if it's the first invoice of this type, e.g. 30 for consultancy services and 1 for accountancy services.

Unfortunately, the auditing system in MS dynamics CRM 2011 is not perfect so deleting invoices, which is actually a custom entity, leaves a record that they were deleted, by whom and at what time, but crucially it does not say which invoice was deleted.

I responded to the tester, after checking the audit view, that he must've deleted the invoices and that explained the incorrect number and closed the ticket.

The tester was not happy, they thought it was a real issue that needed looking at and this is where things normally take a turn for the worse, because we, as developers, assume that the tester did something wrong. 

We don't just assume that the tester did something wrong, of course, we check our code (✔), we run a unit test (✔), we might actually prove to the tester that the functionality that the tester raised a defect about working properly (✔), so why is this insignificant little tester wasting my time rather than accepting that they did something wrong?

It's pretty simple really, they need an explanation that satisfies them

Nobody likes admitting that they are wrong and in cases like this, where the issue, at least to the tester, is intermittent, they would like the developer to prevent it from happening again.

At this points my options were:

  1. Get into a ticket passing ping pong match (which almost certainly would've escalated to ..)
  2. Escalate to PM and/or TM
  3. Reproduce the bug.
So I did, and it turned out that my explanation was only half right. Yes, they had deleted some invoices but there were genuine holes in my explanation.

Is there anything that can be learned from this episode?

  1. Don't be lazy.
  2. Don't assume that the tester made an error
  3. If you are sure that the tester made an error, then see #1 and do replicate the error that they made, who knows they might help you uncover some bugs in the system.

Friday, 7 June 2013

Getting over the 8192 SQL Server truncation limit in SSMS

Today, I was trying to find out whether any workflow was modifying an entity as my memory was somewhat hazy and since I was already logged on the database server, I thought I'd check on the database server, but alas I couldn't as the results were being truncated, which made the query useless.

I whinged a bit to a colleague who simply said:
Why don't you cast the xaml to xml?
My response was, I didn't know that casting the xaml to xml would help, but it does.

select top 1 name, cast (xaml as xml) from workflow
where xaml like '%Process%'

This is the result:


Clicking on the xaml will open a the workflow definition on different window as an xml document.

Sunday, 2 June 2013

More quotes and a joke

In software, we rarely have meaningful requirements. Even if we do, the only measure of success that matters is whether our solution solves the customer’s shifting idea of what their problem is.
Jeff Atwood
Reader, suppose you were an idiot. And suppose you didn’t use version control. But I repeat myself.” 
Mark Twain
A man can never really know whether he isn't sitting in a madhouse. 
Georg Christoph Lichtenberg
A Foolish Consistency is the Hobgoblin of Little Minds 
Ralph Waldo Emerson
The joke

A mathematician and an engineer are sitting in on a string theory lecture. The engineer is struggling, while the mathematician is swimming along with no problem. Finally the engineer asks, "How do you do it? How do you visualize these 11-dimensional spaces?" The mathematician says, "It's easy: first I visualize an n-dimensional space, then I set n equal to 11."