views:

127

answers:

3

Hello,

i have a HttpModule that creates an CommunityPrincipal (implements IPrincipal interface) object on every request. I want to somehow store the object for every request soo i can get it whenever i need it without having to do a cast or create it again.

Basically i want to mimic the way the FormsAuthenticationModule works. It assigns the HttpContext.User property an object which implements the IPrincipal interface, on every request.

I somehow want to be able to call etc. HttpContext.MySpecialUser (or MySpecialContext.MySpecialUser - could create static class) which will return my object (the specific type).

I could use a extension method but i dont know how to store the object so it can be accessed during the request.

How can this be achieved ?

Please notice i want to store it as the specific type (CommunityPrincipal - not just as an object). It should of course only be available for the current request being processed and not shared with all other threads/requests.

Right now i assign my CommunityPrincipal object to the HttpContext.User in the HttpModule, but it requires me to do a cast everytime i need to use properties on the CommunityPrincipal object which isnt defined in the IPrincipal interface.

A: 

Since you already storing the object in the HttpContext.User property all you really need to acheive you goal is a Static method that acheives your goal:-

  public static class MySpecialContext
  {
    public static CommunityPrinciple Community
    {
        get
        {
           return (CommunityPrinciple)HttpContext.Current.User;
        }
    }
  }

Now you can get the CommunityPrinciple as:-

  var x = MySpecialContext.Community;

However it seems a lot of effort to got to avoid:-

  var x = (CommunityPrinciple)Context.User;

An alternative would be an Extension method on HttpContext:-

  public static class HttpContextExtensions
  {
    public static CommunityPrinciple GetCommunity(this HttpContext o)
    {
      return (CommunityPrinciple)o.User;
    }
  }

The use it:-

  var x = Context.GetCommunity();

That's quite tidy but will require you to remember to include the namespace where the extensions class is defined in the using list in each file the needs it.

Edit:

Lets assume for the moment that you have some really good reason why even a cast performed inside called code as above is still unacceptable (BTW, I'd be really interested to understand what circumstance leads you to this conclusion).

Yet another alternative is a ThreadStatic field:-

  public class MyModule : IHttpModule
  {
    [ThreadStatic]
    private static CommunityPrinciple _threadCommunity;

    public static CommunityPrinciple Community
    {
        get
        {
           return _threadCommunity;
        }
    }
    // Place here your original module code but instead of (or as well as) assigning
    // the Context.User store in _threadCommunity.
    // Also at the appropriate point in the request lifecyle null the _threadCommunity

  }

A field decorated with [ThreadStatic] will have one instance of storage per thread. Hence multiple threads can modify and read _threadCommunity but each will operate on their specific instance of the field.

AnthonyWJones
Thanks for your reply. That is what i am already doing :). What I am asking for is if there is a way to avoid the explicit cast everytime i need to use the CommunityPrincipal object. If there is some technique which makes it possible to store it as a CommunityPrincipal and make it available on every request. Etc I was thinking about creating a static class which is "created" per thread (ThreadStaticAttribute), but i dont know if it will be a security problem as I would have to make sure the object is removed at the end of every request as the threads are shared / reused.
MartinF
+1  A: 

I'd recommend you stay away from coupling your data to the thread itself. You have no control over how asp.net uses threads now or in the future.

The data is very much tied to the request context so it should be defined, live, and die along with the context. That is just the right place to put it, and instantiating the object in an HttpModule is also appropriate.

The cast really shouldn't be much of a problem, but if you want to get away from that I'd highly recommend an extension method for HttpContext for this... this is exactly the kind of situation that extension methods are designed to handle.

Here is how I'd implement it:

Create a static class to put the extension method:

public static class ContextExtensions
{
    public static CommunityPrinciple GetCommunityPrinciple(this HttpContext context)
    {
        if(HttpContext.Current.Items["CommunityPrinciple"] != null)
        {
            return HttpContext.Current.Items["CommunityPrinciple"] as CommunityPrinciple;
        }
    }
}

In your HttpModule just put the principal into the context items collection like:

HttpContext.Current.Items.Add("CommunityPrincipal", MyCommunityPrincipal);

This keeps the regular context's user property in the natural state so that 3rd party code, framework code, and anything else you write isn't at risk from you having tampered with the normal IPrincipal stroed there. The instance exists only during the user's request for which it is valid. And best of all, the method is available to code as if it were just any regular HttpContext member.... and no cast needed.

Stephen M. Redd
Thanks for your reply.I store the object as a IPrincipal on the context.user and then use extension method to do an explicit cast to whatever type. It seems to be the best i can get. I hoped that it was possible to somehow make a property of a specific type that i could use like context.user instead of having the extension methods or storing it as an object on the HttpContext via HttpContext.Items. Apparently not.
MartinF
A: 

Assigning your custom principal to Context.User is correct. Hopefully you're doing it in Application_AuthenticateRequest.

Coming to your question, do you only access the user object from ASPX pages? If so you could implement a custom base page that contains the cast for you.

public class CommunityBasePage : Page
{
    new CommunityPrincipal User
    {
     get { return base.User as CommunityPrincipal; }
    }
}

Then make your pages inherit from CommunityBasePage and you'll be able to get to all your properties from this.User.

batwad
Thanks for your reply.This is also a good idea. I will normally use it in either the controller or in the view soo i can create a custom view and custom controller that i can inherit from. Thanks.
MartinF