views:

387

answers:

2

How do you unit test code decorated with the PrincipalPermission attribute?

For example, this works:

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
        var c = new MyClass();
    }
}

[PrincipalPermission(SecurityAction.Demand, Role = @"BUILTIN\Users")]
class MyClass
{
    public MyClass()
    {
        Console.WriteLine("This works.");
    }
}

This throws a SecurityException:

[TestClass]
public class UnitTest1
{
    [TestInitialize]
    public void TestInitialize()
    {
        AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
    }

    [TestMethod]
    public void TestMethod1() 
    { 
        var c = new MyClass();
    }
}

Any ideas?

+1  A: 

You could try impersonating different users within the test method, if you run the code as an admin you could create a local user account inside the test (or test class) and delete it at the end.

Edit: Sorry, I imagined using impersonate to test a failure case - I should have read your question properly :) I have similar unit tests, and they are able to create local accounts within mstest. Whether this is good practice is another matter.

I see you already did as this page suggests: set the app domain's principal policy to "WindowsPrincipal". For me, Thread.CurrentPrincipal.Identity.Name gives my user name and the test passes using VS 2005 and VS 2008 targetting .NET 2.0, 3.0 & 3.5.

Are you running on Vista/Win7 with UAC and non elevated VS? Otherwise are you able to repro either on another machine, using a different group or by creating another local admin account on your machine and running the tests as this user?

Alex Peck
When I run the test, the CurrentPrincipal has an unauthenticated GenericIdentity so I don't believe the test code would be able to create or delete a local account.
Mark Good
I am running VS on XP as admin. Ray's suggestion worked, but left me with other security concerns. See the comments that I left Ray.
Mark Good
+3  A: 

How about creating a GenericIdentity and attaching that to the Thread.CurrentPrincipal in your test like so:

[TestMethod]
public void TestMethod1() 
{ 
    var identity = new GenericIdentity("tester");
    var roles = new[] { @"BUILTIN\Users" };
    var principal = new GenericPrincipal(identity, roles);
    Thread.CurrentPrincipal = principal;

    var c = new MyClass();
}

For a fail test, you could:

[TestMethod]
[ExpectedException(typeof(SecurityException))] // Or whatever it's called in MsTest
public void TestMethod1() 
{ 
    var identity = new GenericIdentity("tester");
    var roles = new[] { @"BUILTIN\NotUsers" };
    var principal = new GenericPrincipal(identity, roles);
    Thread.CurrentPrincipal = principal;

    var c = new MyClass();
}
Ray Vernagus
This worked, Ray, Thanks! But it seemed too easy to fake. Couldn't anyone claim to have the require role?
Mark Good
Cool! Glad to help! It is just that easy in a full trust setting. Setting Thread.CurrentPrincipal requres special privleges, specifically, you need SecurityPermission of SecurityPermissionFlag.ControlPrincipal. See Keith Brown's book online: http://alt.pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsThreadDotCurrentPrincipal.html
Ray Vernagus
Very interesting article! But I have no way of knowing or controlling whether or not my users have the security permission to change CurrentPrincipal. Luckily, my application is a WCF service which I am guessing (and hoping), like the timer and thead pool, won't propagate the CurrentPrincipal.
Mark Good