views:

180

answers:

2

Using Unity in an ASP.Net MVC 2 app I have various dependencies on Controllers instantiated correctly. However, I want to ensure that the current IPrincipal for the user is going to be passed via injection to lower level Services, Repository etc.

Therefore in a lower level service I have something like:

[Dependency] IPrincipal CurrentUser {get; set;}

If I use Property Dependency Injection I do not get what I want because the Controller is instantiated BEFORE a User principal is available and in any case Unity does not know to get the current user credentials.

So what I want is to be able to inject the current user's IPrincipal (or probably RolePrincipal) into one of the dependencies for the Controller.

How can I do this?

+3  A: 

Why not take the direct route, and just assign it.

Thread.CurrentPrincipal = user;

Dependency injection is good, but don't let it get in the way of the best dependency injector, the programmer.

mrjoltcola
+1 Thread.CurrentPrincipal is already the de-facto standard for dealing with the current user, and there are rarely reasons for deviating from this well-established coding idiom.
Mark Seemann
The application is multi-tier and extremely likely to be deployed into multi-tiers such that I have an application CustomPrincipal that can be passed through to avoid any pollution of mid/back-end tiers with HttpContext dependencies. Assuming that there will be a WCF communication from Web Presentation (MVC Controller) to mid tier services, can I be sure that the Thread.CurrentPrincipal will return the WebForms authenticated IPrincipal?
Redeemed1
A: 

Why inject it? The current principal is already present as User. That is what we use, and it works fine so far. The user shouldn't change within a single request, should it?

protected void Application_AuthenticateRequest()
{
    var ticket = GetAuthenticationTicket();
    // Perform actual authentication, etc.
    MyUser user = BigAuthStuff();
    Context.User = user;
    Thread.CurrentPrincipal = user;
}

public class MyBaseController : Controller
{
    protected MyUser AuthenticatedUser
    {
        get { return User as MyUser; }
    }
}
mnemosyn
Context.User doesn't work very well if the web site calls into a technology-agnostic Domain Model. It would be a grave error for a Domain Model to attempt to access any kind of HTTP Context.
Mark Seemann
Hm, indeed. However, I don't think the Domain Model should interact with some static outside anyway. The idea is to put the user into the context so the controller can work with it. Ultimately, it should be the controller who's calling into the Domain Model, right? Perhaps you could go even further and say: The Domain Model should not have a notion about users executing code at all. If the Domain Model is concerned with authorization, it needs to include just that functionality itself, so we'd have to set-up the domain model based on the current user, which we again should do in controller?!
mnemosyn
@mnemosyn the issue is passing the credentials all the way through over a multi-tier deployment via wcf and bringing some other attributes (e.g. Roles) along in a Custom Principal
Redeemed1
Hm, I haven't used WCF so I can hardly comment on that. Perhaps you should have said that in the original question? For a 'normal' MVC application my approach works fine and it also has Roles, etc. in a custom IPrincipal. Although I have to admit that getting the User in `Application_AuthenticateRequest()` is not exactly smart, because it is often not necessary to perform authentication, i.e. for images. Then again, using CDN that should be a no-issue...
mnemosyn