views:

276

answers:

1

I've a SecurityService that has a AutoLogin method, that uses the ServiceSecurityContext to find out which windows identity is calling and then tries to find the related user account in the database. This is working fine when it is called from a web site that uses impersonation and requires integrated security in IIS. The call is using the stock NetPipeBinding.

I'd like to test the service as follows:

[TestMethod]
public void AutoLoginAsAnonymousFails()
{
    using (var anonymousContext = WindowsIdentity.Impersonate(WindowsIdentity.GetAnonymous().Token))
    {
     ISecurityService securityService = ClientChannelManager.CreateSecurityServiceChannel();
     var loginResponse = securityService.AutoLogin();
     ((ICommunicationObject)securityService).Close();
     Assert.IsFalse(loginResponse.IsSuccessful);    
    }
}

On the service side the user in the securitycontext is always me - how to make it an anonymous user? I've already tried to impersonate the IntPtr.Zero but without success.

For reference the relevant part of the service method:

public ResponseMessage AutoLogin()
{
    if (ServiceSecurityContext.Current.WindowsIdentity != null
        && !ServiceSecurityContext.Current.WindowsIdentity.IsAnonymous
        && !ServiceSecurityContext.Current.WindowsIdentity.IsGuest
        && ServiceSecurityContext.Current.WindowsIdentity.IsAuthenticated)
    {
     // find the user based on his windows identity and return success = true message
    }

    // return success = false message
}
A: 

This is a classic example of how separation of concerns could help you. Instead of relying directly on ServiceSecurityContext (something that one should, IMO, never do), make sure to configure your service so that security information is instead encapsulated in Thread.CurrentPrincipal.

IIRC, when you use Windows authentication and impersonation, it may even set this up for you automatically, but otherwise, you can always write a custom ServiceAuthorizationManager, that does this for you.

This will allow you to vary your security concerns independently of your domain logic. If you stick with the IPrincipal interface and resist the temptation of downcasting to WindowsPrincipal, your code will even be ready for the future of identity: Claims-based Identity, as implemented by the Windows Identity Foundation (WIF).

This also helps tremenduously with unit testing, because you can then just assign a GenericPrincipal to Thread.CurrentPrincipal before invoking your System Under Test (SUT).

Mark Seemann
well, Mark (btw: nice name, but written wrong :-p), thank you for your input so far. You're probably right. I'll check this in a separate test project. But I still have no idea why the test is unable to simulate an anonymous user although I'm inside an impersonating block. Any ideas why this fails?
Marc Wittke
I'll need a bit more input before I can attempt to answer that. When I originally wrote my answer I assumed that the unit test simply created a new instance of the service class itself, thus bypassing the service host. I based that assumption on your use of the term 'unit test', but I now see that your code may also read as if your test is creating a new proxy and invoking your service (hosted ouf of process) via NetNamedPipes. Is that correct a more correct interpretation?
Mark Seemann
It's correct. In detail I am using the InProcFactory from Juval Löwys ServiceModelEx library. It allows to replace the new keyword by a factory call - nice stuff to see if the serialization stuff is working. And yes, this is not the fine granularity that is desirable for unit tests, but belive me - implementing such a type of test is by amplitudes better than what we had before ...
Marc Wittke
I have no problem with the granularity of your testing efforts - I just misunderstood the scenario. Can you get it to work with netTcpBinding instead (just to troubleshoot)?
Mark Seemann