2008-09-30

Unit testing security

Following on from my previous post about using(Tricks) here is an example which makes writing test cases easier rather than just for making your code nicely formatted. Take a look at the following test which ensures Article.Publish sets the PublishedDate correctly:

[TestMethod]
public void PublishedDateIsSet()
{
  //Create the EcoSpace, set its PMapper to a memory mapper
  var ecoSpace = TestHelper.EcoSpace.Create();

  //Creat an article
  var article = new Article(ecoSpace);

  //Create our Rhino Mocks repository
  var mocks = new MockRepository();

  //Mock the date/time to give us a predictable value
  var mockDateTimeService = mocks.StrictMock<IDateTimeService>();
  ecoSpace.RegisterEcoService(typeof(IDateTimeService), mockDateTimeService);

  //Get a date/time to return from the mock DateTimeService
  var now = DateTime.Now;
  using (mocks.Record())
  {
    //When asked, return the value we recorded earlier
    Expect.Call(mockDateTimeService.Now).Return(now);
  }

  //Check mockDateTimeService.Now is called from Article.Publish
  using (mocks.Playback())
  {
    article.Publish();
  }

  //Check the date/time from IDateTimeService is stored in PublishedDate
  Assert.AreEqual(now, article.PublishedDate);
}


The method it is testing

public void Publish()
{
  IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;
  var dateTimeService = serviceProvider.GetEcoService<IDateTimeService>();
  PublishedDate = dateTimeService.Now;
}


Now at some point in the future you decide to enforce some security within your business objects. You decide that only certain people can publish your article, such as the author or an administrator. This will now break every test you have which assumes article.Publish will just work.


public void Publish()
{
  IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;

  var currentUserService = serviceProvider.GetEcoService<ICurrentUserService>();
  var currentUser = currentUserService.CurrentUser;
  if (currentUser != this.Author && !currentUser.HasRole<SystemAdministratorRole>())
    throw new SecurityException("Cannot publish this article");

  var dateTimeService = serviceProvider.GetEcoService<IDateTimeService>();
  PublishedDate = dateTimeService.Now;
}


This would dramatically complicated any test which relied on having a published article. Each time you would additionally have to:
01: Create a user.
02: Mock ICurrentUserService to return that user.
03: Ensure the article.Author is set to that user, or the user owns a SystemAdministratorRole.

If you have 20 tests requiring a published article this is going to cause you a lot of work! The first mistake here is that the article is testing for permissions; permission granting should be a service. I would separate the service out like so...

public interface IPermissionService
{
  bool MayPublishArticle(Article article);
}


Your EcoSpace would have a class implementing this interface and return the appropriate result, the EcoSpace would register this default service.

public class PermissionService : IPermissionService
{
  IEcoServiceProvider ServiceProvider;
  
  public PermissionService(IEcoServiceProvider serviceProvider)
  {
    ServiceProvider = serviceProvider;
  }

  public bool MayPublishArticle(Article article)
  {
    var currentUserService = serviceProvider.GetEcoService<ICurrentUserService>();
    var currentUser = currentUserService.CurrentUser;
    return currentUser == article.Author
      || currentUser.HasRole<SystemAdministratorRole>());
  }
}


In the EcoSpace:
public override bool Active
{
  get { return base.Active; }
  set
  {
    if (value && !Active)
      RegisterDefaultServices();
    base.Active = value;
  }
}

private void RegisterDefaultServices()
{
  RegisterEcoService(typeof(IPermissionService), new PermissionService(this));
  RegisterEcoService(typeof(ICurrentUserService), new CurrentUserService());
  RegisterEcoService(typeof(IDateTimeService), new DateTimeService());
}


Now Article.Publish looks like this

public void Publish()
{
  IEcoServiceProvider serviceProvider = AsIObject().ServiceProvider;
  var permissionService = serviceProvider.GetEcoService<IPermissionService>();

  if (!permissionService.MayPublishArticle(this))
    throw new SecurityException("Cannot publish this article");

  var dateTimeService = serviceProvider.GetEcoService<IDateTimeService>();
  PublishedDate = dateTimeService.Now;
}


But how does this help? The first advtange is that we have separated the PermissionService so that we can test it in isolation, but we would still need to mock the IPermissionService wouldn't we? Yes we would! But how does this look?

using (TestHelper.Permissions.PermitAll(ecoSpace))
{
  article.Publish();
}


Much more simple eh? To achieve this I have a static class named Permissions in my test project

public static class Permissions
{
  private class TestPermissionService : IPermissionService
  {
    (simple code omitted)
    Accept a boolean in the constructor, and return
    it for every method call.
  }

  public static IDisposable Allow(MyEcoSpaceType ecoSpace)
  {
    var originalService = ecoSpace.GetEcoService<IPermissionService>();
    var newService = new TestPermissionService(true);
    ecoSpace.RegisterEcoService(typeof(IPermissionService), newService);
    return DisposableAction(
      () => ecoSpace.RegisterEcoService(typeof(IPermissionService), originalService)
    );
  }
}


All this does is to record the current IPermissionService, register a new one which always returns True/False (depending on what we pass to its constructor), and then return an instance of DisposableAction. To this instance we pass an Action which re-registers the original service. The action is called when IDisposable.Dispose is called:

public class DisposableAction : IDisposable
{
  Action Action;
  public DisposableAction(Action action)
  {
    Action = action
  }

  void IDisposable.Dispose()
  {
    Action();
  }
}



So the following line

using (TestHelper.Permissions.PermitAll(ecoSpace))
{
  article.Publish();
}


will
01: Record the original IPermissionService.
02: Register a new one which always returns True.
03: Execute the code within the Using { } block.
04: IDisposable.Dispose will be called on my DisposableAction.
05: The previous service will be restored.

Single instance application - revisited

Not so long ago I posted a solution to having a single-instance application. Rather than just preventing secondary instances from running the requirement was to have the 2nd instance pass its runtime parameters onto the 1st instance so that it can process them. My solution used remoting on the local machine. This appeared to work very well until recently when I needed an OpenFileDialog. Attempting to show the dialog resulted in an error about COM thread apartments. So, it wasn't THE solution.

After a bit of research I decided to use named pipes instead. This meant I had to upgrade my app from .NET 2 to 3.5, but I think it is worth it. To implement the feature in an app you need to do 2 things. First you need to realize the interface ISingleInstanceApplicationMainForm on your app's main form in order to accept command line arguments from any subsequently started instances. Next you need to change your Program.Main method like so:

[STAThread]
static void Main(string[] args)
{
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  new SingleInstanceApplication<MainForm>("CompanyName.ApplicationName", args);
}



The source code for SingeInstanceApplication is an adaptation of some code I read here; and here it is:


public interface ISingleInstanceApplicationMainForm
{
  void AcceptCommandLineArguments(string[] args);
}

public class SingleInstanceApplication<TForm> : IDisposable
  where TForm : ISingleInstanceApplicationMainForm
{
  Mutex Mutex;
  bool IsFirstInstance;
  string ApplicationUniqueID;
  protected TForm MainForm { get; private set; }

  public SingleInstanceApplication(string applicationUniqueID, string[] args)
  {
    ApplicationUniqueID = applicationUniqueID;
    Mutex = new Mutex(true, ApplicationUniqueID, out IsFirstInstance);
    if (IsFirstInstance)
    {
      MainForm = (TForm)Activator.CreateInstance(typeof(TForm));
      (MainForm as ISingleInstanceApplicationMainForm).AcceptCommandLineArguments(args);
      ThreadPool.QueueUserWorkItem(new WaitCallback(ListenForArguments));
      Application.Run((Form)(object)MainForm);
    }
    else
      PassArgumentsToFirstInstance(args);
  }

  private void ListenForArguments(Object state)
  {
    try
    {
      using (var server = new NamedPipeServerStream(ApplicationUniqueID))
      {
        using (var reader = new StreamReader(server))
        {
          server.WaitForConnection();

          string[] args = reader.ReadLine().Split(new char[] {'\t'}, StringSplitOptions.RemoveEmptyEntries);
          ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveArgumentsFromNewInstance), args);
        }//using reader
      }//using server
    }
    catch (IOException) { }
    finally
    {
      ListenForArguments(null);
    }
  }

  private void ReceiveArgumentsFromNewInstance(Object state)
  {
    string[] args = (string[])state;
    (MainForm as Form).Invoke(
      new ThreadStart(
        delegate()
        {
          MainForm.AcceptCommandLineArguments(args);
        }
        )
      );
  }

  private void PassArgumentsToFirstInstance(string[] args)
  {
    var builder = new StringBuilder();
    foreach (var arg in args)
      builder.AppendFormat("{0}\t", arg);

    try
    {
      using (var client = new NamedPipeClientStream(ApplicationUniqueID.ToString()))
      {
        using (var writer = new StreamWriter(client))
        {
          client.Connect(500); // 0.5 seconds timeout
          writer.WriteLine(builder.ToString());
        }//using writer
      }//using client
    }
    catch (TimeoutException) { }
    catch (IOException) { }
  }




  void IDisposable.Dispose()
  {
    if (IsFirstInstance && Mutex != null)
      Mutex.ReleaseMutex();
  }

}

2008-09-24

using(TricksToFormatYourCodeNicely)

I've been writing a data importer which takes a specific data input format and outputs XML, this XML is then imported within my application. What annoyed me was the way in which the source code was formatted....


writer.WriteStartElement("data");
writer.WriteAttributeString("1", "1");
writer.WriteAttributeString("2", "2");
writer.WriteAttributeString("3", "3");

writer.WriteStartElement("systemData");
writer.WriteAttributeString("a", "a");
writer.WriteAttributeString("b", "b");
writer.WriteEndElement();//systemData

writer.WriteEndElement();//data


It just didn't look nice. I thought about splitting it into separate methods, but most of the time this would have been overkill as the methods would have been very short. Instead I wrote an extension method on XmlWriter:


public static class XmlWriterHelper
{
  public static IDisposable StartElement(this XmlWriter writer, string elementName)
  {
    return new DisposableElementWriter(writer, elementName);
  }

  private class DisposableElementWriter : IDisposable
  {
    private XmlWriter Writer;

    public DisposableElementWriter(XmlWriter writer, string elementName)
    {
      Writer = writer;
      Writer.WriteStartElement(elementName);
    }

    public void Dispose()
    {
      Writer.WriteEndElement();
    }
  }
}



Now I can write code like this instead:

using (writer.StartElement("data"))
{
  writer.WriteAttributeString("1", "1");
  writer.WriteAttributeString("2", "2");
  writer.WriteAttributeString("3", "3");
  using (writer.StartElement("systemData"))
  {
    writer.WriteAttributeString("a", "a");
    writer.WriteAttributeString("b", "b");
  }//systemData
}//data



Less code AND easier to read. What a bonus!

2008-09-17

Parameterised queries in ECO

Whenever I generate OCL queries in code I find myself having to escape user input in order to avoid making the query invalid, or allowing malicious input.

I've decided instead to use the ECO equivalent of parameterised queries (variables in ECO) and here is the result.

public static string CreateParameterisedQuery(
  this IEcoServiceProvider serviceProvider,
  string query,
  out IModifiableVariableList vars,
  params object[] args)
{
  vars = serviceProvider.GetEcoService<IVariableFactoryService>().CreateVariableList();
  for (int varIndex = 0; varIndex < args.Length; varIndex++)
  {
    string variableName = "autoVar_" + varIndex.ToString();
    query = query.Replace("{" + varIndex.ToString() + "}", variableName);
    vars.AddConstant(variableName, args[varIndex]);
  }
  return query;
}



To use this code you would do something like this

//1: Create the OCL with string.format style parameters
string query = "Person.allInstances" +
  "->select(name.sqlLikeCaseInsensitive({0}))" +
  "->select(gender = {1}";

//2: Parse the query and build the variable list
IModifyableVariableList vars;
query = self.AsIObject().ServiceProvider.CreateParameterisedQuery(query,
  out vars,
  "Peter Morris",
  Gender.Male);


Now you can use IOclPsService or IOclService to execute the new query passing the variables.

2008-09-08

User authentication in SilverLight

I wanted to know how to authenticate users in a SilverLight app using their Windows login info.

01: Set the authentication mode to Windows and <deny users="?"/> in <system.web> within web.config
02: Move the silverlight control to Default.aspx and set that as your start page
03: Add the following Page_Load code

  protected void Page_Load(object sender, EventArgs e)
{
IPrincipal p = HttpContext.Current.User;
if (p == null)
throw new SecurityException("No current user");
if (!(p is WindowsPrincipal))
throw new SecurityException("Not a windows user");
if (!p.Identity.IsAuthenticated)
throw new SecurityException("Not authenticated");
Xaml1.InitParameters =
string.Format("user={0},session={1}", p.Identity.Name, Session.SessionID);
}


04: In app.xaml.cs you can now read the InitParameters using e.InitParameters in the Application_Startup method.