views:

103

answers:

2

I'm wrestling with building a custom membership provider that allows me to have the security scheme I need.

I have my own IPrincipal, IIdentity, and MembershipProvider. I have the authentication working fine. The problem I'm running into now is the authorization.

The problem I have with the authorization scheme is inherintly in the IPrincipal's IsInRole behavior. This behavior is tightly coupled to various features of ASP.NET Webforms, my main concern being the sitemap authorization, because I'd like to use it if I can.

So you might traditionally have a sitemap like so:

<siteMap xmlns="blahblah">
    <siteMapNode url="PersonView.aspx" 
        title="View Person" 
        description="View the details of a person" 
        roles="ViewerRole" />
</siteMap>

Here, anyone trying to go to the PersonView.aspx page will be required to have the ViewerRole. This is where my problem arises. I don't want to have my authorization tied to a user's role. Instead, I want the authorization to be tied to the behavior I'm performing and let some underlying thing behind the scenes take care of the authorization.

So what I really want is something like this instead:

<siteMap xmlns="blahblah">
    <siteMapNode url="PersonView.aspx" 
        title="View Person" 
        description="View the details of a person" 
        roles="Person|View" />
</siteMap>

This should be interpreted as anyone trying to go to the PersonView.aspx page must have View rights to the Person business object.

I already have an Authorizer object which has a signature like so:

public static bool Authorize(Type type, Access access, IUser user)

Which will take, for instance, the Person type, the View access (enum), and the user to check it against. Now, I have the code inside the Authorize figured out.

My problem is, how do I get from the IPrincipal's IsInRole to my Authorize? I've tried different things but none of them seem to work. I really don't like the magic string approach, but it seems like I'm stuck with it. If there was a way to build it in a strongly typed manner I would definitely prefer that instead. Is there a better way to do this that I'm not thinking of?

+1  A: 

I figured out a pretty clean way to solve my problem. What I did was change my Authorize signature to accept an enumeration instead of the type, and use that in the IsInRole to determine permission.

So I have:

public static bool Authorize(AuthorizationType type, Access access, IUser user)

and then I use it in IsInRole like so:

public bool IsInRole(string role)
{
    var typeAndAccess = role.Split('|');
    var authType = 
        (AuthorizationType)Enum.Parse(
            typeof(AuthorizationType), typeAndAccess[0]);
    var access = (Access)Enum.Parse(typeof(Access), typeAndAccess[1]);

    return Authorizer.Authorize(
        authType, access, Context.User.Identity as IUser);
}

This allows me to use the magic string approach when I absolutely have to (like in a sitemap), but it also allows me a more strongly typed approach when I'm using it programmatically like:

void Page_Load(...)
{
    if (Context.User.IsInRole(AuthorizationType.Person + '|' + Access.View)
    {
        //I have view rights, do some stuff
    }   
}
Joseph
+1  A: 

Another way I have seen this sort of thing done is when initalizing the user to store all rights and objects as a roles in the Principal. Depending on the number of objects you have this might be another option. Essentially you'd get rid of the Authorizer.Authorize call, and store

  • view|Person
  • Edit|Person
  • add|Peron

As three different roles. I've seen this used in a system which had a heirarchy of features so you might have Feature A -> Feature B, C or D for example where B C and D can only exist if you have A. This could then be roles such as:

  • A
  • A|B or A|C or A|D

So now your code can check if they A or if you need to check subfeature you can check A|B.

Suggestion

To make your impl even nicer in your page load What if you did:

if (Context.User.IsInRole(
       PermissionFactory.CreateToken(AuthorizationType.Person,Access.View)))  
{        
  //I have view rights, do some stuff    
}

Now you've hidden the fact that this is a string completly.

JoshBerke
Thanks for your suggestion Josh, I like the idea of having a PermissionFactory to isolate away the magic string generation. However, the token generation wouldn't really be useful in a sitemap. How would you deal with sitemaps in this scenario? Keep them magic strings?
Joseph
If you dynamically build your site map you could hide the strings but at the end of the day it's still going be a string. I've never liked how the roles worked. Roles are so broad, you always need a Role and a Capability...wish this was baked into the model but ahh well.
JoshBerke
@Josh Yeah I'm having to fight it the entire way because my paradigm is fundamentally different. It's really annoying.
Joseph