Extending a MembershipUser
You
are creating a blogging application where users each have a blog. Blogs are clustered into categories of
blogs. A user can have roles in
more than 1 blog.
Category
A
Blog A
User
1 **
Blog B
User
2
Category
B
Blog C
User
1 **
Blog D
User
3
**
Note that User 1 has 2 blogs.
At
runtime you want to be able to determine the following information about a
user:
- Their username
- Their user id
- Whether they are a member
of a given blog
- What role(s) they have in
a particular blog
There's
a complex issue here dealing with row-level security on data and, at runtime,
being able to programmatically determine the permissions at that level. At runtime, you want to have code which
looks similar to this to determine whether to grant or deny access to a
particular item:
if(
PermissionAPI.UserCanEdit( blogId ) ) {
// access granted
}
The
question here is "where to hang your permission data so that you have access to
it at runtime".
Is
there anything baked into the Roles API that you can override so that it handles
not only an array of string roleNames but also complex type permissions or, is
the intended usage in this scenario to implement your own custom MembershipUser
class to add the rights to?
A
workaround for using the Roles API by dynamically creating roles
The
Roles API in ASP.NET 2.0 works with a string[] of roles. For example, John Doe might have the
following roles: "Administrator", "Editor", etc&
But,
what if John has different roles on a per category or per group basis? Like, John is an "Administrator" in
Group A but is only an "Editor" in Group B.
The
quick-n-dirty way around this might be to dynamically create custom role strings
at the time that you are managing that users permissions. In the previous example, you might
dynamically create the following 2 role strings:
GroupA$Administrator
GroupB$Editor
Using
this method would allow you to leverage the built in
Roles.IsUserInRole(roleName) API logic to determine whether access should be
denied or granted.
Sample
implementation code for this method:
static
bool UserCanEditBlog( blogId ) {
return (Roles.IsUserInRole(
blogId.ToString() + "$Editor" ) ||
Roles.IsUserInRole( blogId.ToString() + "$Administrator" ) ;
}
Another
method might be to extend the current user so that the permissions can be
queried directly via the current Identity.
To do this you may have to create a custom MembershipProvider which will
allow you to return custom MembershipUsers with the custom permission data
tagged as an array of permissions to that user.
Using
the Membership API to extend the user
//
Create a class for the new custom user and extend it with our custom permission
data
class
BlogUser : MembershipUser {
// this is a convenience thing because the
SqlMembershipProvider uses MembershipUsers
.ctor(
MembershipUser baseUser )
.ctor(
MembershipUser baseUser, Dictionary<int, BlogPermission> permissions
)
// the new
custom property
Dictionary<int, BlogPermission> _permissions ;
public
Dictionary<int, BlogPermission> Permissions { get{ return _permissions ; }
}
}
//
override each method to populate Permissions on the BlogUser class
class
BlogSqlMembershipProvider : SqlMembershipProvider {
public override MembershipUser CreateUser() {
MembershipUser baseUser = base.CreateUser(...) ;
// do stuff to get permission
details
BlogPermission[] perms = GetBlogPermissions() ;
BlogMembershipUser blogUser = new BlogMembershipUser( baseUser, perms )
;
return blogUser ;
}
}
Where
BlogPermissions looks like this:
class
BlogPermission {
int BlogId
string[] Roles
}
Sample
implementation code for this method:
static
bool UserCanEditBlog( blogId ) {
BlogPermission perms =
((BlogUser)User).Permissions[blogId]
as BlogPermission ;
if( perms != null ) {
return perms.Roles.Contains("Editor") ;
}
return false ;
}
NOTE: I have a working prototype of
this here: http://projectdistributor.net/Projects/Project.aspx?projectId=73