How to Implement Dynamic Roles

M

Mike Hofer

BACKGROUND: We've designed a Website for a client that will be
deployed across multiple physical locations. The site will be hosted
from a corporate NOC, and administered by the IT group there.

The site's end-users tend to fill each others' roles on a pretty
frequent basis. Their permissions in the system have to be extremely
flexible. We initially wanted to use role-based security, defining
concrete roles, but when we realized how these folks worked, we
realized that that simply wouldn't work. Joe would one day be out
sick and Mary would sit in his desk and do his work for him. This
happens a *lot* in this particular company, and she would have to be
able to acquire his permissions. (Not very secure, but that's what
they want.)

As a result of this, we opted to skip the roles in the code (because
they couldn't really be concretely defined), and check for the
existence of *permissions* on the user. This check occurs on each page.
We don't really look for what roles the user is in; instead, we check
to see if he has a specific permission. If he has it, access is
granted; otherwise, he's presented with the Access Denied page. (The
menu system, a custom control, prevents the users from accessing these
pages in the first place, but the pages contain fail-safe code to
prevent the users from hard-coding the URLs as well.)

PROBLEM: The IT group has recently demanded that the system implement
role-based security. This has fairly stumped us. They want to be able
to create roles in the system, apply permissions to the role, and then
place the users in the role. The system should then check to see if the
user is in the role.

The problem here, as I see it, is that the roles are then *dynamic*,
and the role-based security in .NET isn't really dynamic in nature.
When you design a Web page, you kind of have an idea of what roles you
want to access it. For instance, assume you have a form that allows you
to fill out payroll forms. Generally speaking, you only want
individuals in the PayrollEmployees role to access that page, and
that's something you know before the site is designed and deployed.
So you can write code like this:

If Not user.IsInRole("PayrollEmployees") Then
Response.Redirect("~/AccessDenied.aspx")
End If

But how do you do this when you don't know the names of the roles
beforehand?

HYPOTHESIS: Bear in mind that this stuff all has to be done within our
application's interface, and that we need to be able to manage the
permissions within the database. The application has a lookup table
with the permissions in it. We could create a series of tables like
this:

+--------------------+
| Role |
+--------------------+
| ID (int) (PK) |
| Name (varchar[255])|
+--------------------+

+-------------------------+
| RolePermission |
+-------------------------+
| RoleID (int) (FK) | <-- Foreign key into Role table
| PermissionID (int) (FK) | <-- Foreign key into Permission table
+-------------------------+

+-------------------+
| UserRole |
+-------------------+
| UserID (int) (FK) | <-- Foreign key into User table
| RoleID (int) (FK) | <-- Foreign key into Role table
+-------------------+

The code is currently liberally sprinkled with calls to a method
(User.HasPermission), which takes a permission as an argument. This
method invokes a stored procedure which makes the determination. The
stored procedure can be rewritten to first get all the roles that have
the permission, and then determine whether or not the user is a member
of any of those roles. If the user is a member of any of those roles,
the method returns True.

QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?
 
C

Carl Daniel [VC++ MVP]

Mike said:
QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?

I don't have much to offer except that having written a large role-based
system, I'm quite unimpressed with the ASP.NET role-based security. It's
too limited to be of much use in any application with non-trivial security
needs.

Your proposed solution sounds like it might be a viable path for you. I
wouldn't design the application that way to start with, but since you
already have lots of code, the impact of the change is likely a significant
factor.

Don't be too concerned about the efficiency. Unless you have 10's of
millions of users/roles, all of the user/role/join tables will be in memory
in the database all the time and those queries will be very fast.

You might (I stress might) want to look into writing a new RoleProvider of
your own. I'm not 100% sure that there's really a solution down that path,
since my impression is that the whole conceptual model for the role-based
security in ASP.NET is weak, but it may be something worth looking into.

-cd
 
E

Edwin Knoppert

What part is weak?


Carl Daniel said:
I don't have much to offer except that having written a large role-based
system, I'm quite unimpressed with the ASP.NET role-based security. It's
too limited to be of much use in any application with non-trivial security
needs.

Your proposed solution sounds like it might be a viable path for you. I
wouldn't design the application that way to start with, but since you
already have lots of code, the impact of the change is likely a
significant factor.

Don't be too concerned about the efficiency. Unless you have 10's of
millions of users/roles, all of the user/role/join tables will be in
memory in the database all the time and those queries will be very fast.

You might (I stress might) want to look into writing a new RoleProvider of
your own. I'm not 100% sure that there's really a solution down that
path, since my impression is that the whole conceptual model for the
role-based security in ASP.NET is weak, but it may be something worth
looking into.

-cd
 
N

.neter

Mike said:
BACKGROUND: We've designed a Website for a client that will be
deployed across multiple physical locations. The site will be hosted
from a corporate NOC, and administered by the IT group there.

The site's end-users tend to fill each others' roles on a pretty
frequent basis. Their permissions in the system have to be extremely
flexible. We initially wanted to use role-based security, defining
concrete roles, but when we realized how these folks worked, we
realized that that simply wouldn't work. Joe would one day be out
sick and Mary would sit in his desk and do his work for him. This
happens a *lot* in this particular company, and she would have to be
able to acquire his permissions. (Not very secure, but that's what
they want.)

As a result of this, we opted to skip the roles in the code (because
they couldn't really be concretely defined), and check for the
existence of *permissions* on the user. This check occurs on each page.
We don't really look for what roles the user is in; instead, we check
to see if he has a specific permission. If he has it, access is
granted; otherwise, he's presented with the Access Denied page. (The
menu system, a custom control, prevents the users from accessing these
pages in the first place, but the pages contain fail-safe code to
prevent the users from hard-coding the URLs as well.)

PROBLEM: The IT group has recently demanded that the system implement
role-based security. This has fairly stumped us. They want to be able
to create roles in the system, apply permissions to the role, and then
place the users in the role. The system should then check to see if the
user is in the role.

The problem here, as I see it, is that the roles are then *dynamic*,
and the role-based security in .NET isn't really dynamic in nature.
When you design a Web page, you kind of have an idea of what roles you
want to access it. For instance, assume you have a form that allows you
to fill out payroll forms. Generally speaking, you only want
individuals in the PayrollEmployees role to access that page, and
that's something you know before the site is designed and deployed.
So you can write code like this:

If Not user.IsInRole("PayrollEmployees") Then
Response.Redirect("~/AccessDenied.aspx")
End If

But how do you do this when you don't know the names of the roles
beforehand?

HYPOTHESIS: Bear in mind that this stuff all has to be done within our
application's interface, and that we need to be able to manage the
permissions within the database. The application has a lookup table
with the permissions in it. We could create a series of tables like
this:

+--------------------+
| Role |
+--------------------+
| ID (int) (PK) |
| Name (varchar[255])|
+--------------------+

+-------------------------+
| RolePermission |
+-------------------------+
| RoleID (int) (FK) | <-- Foreign key into Role table
| PermissionID (int) (FK) | <-- Foreign key into Permission table
+-------------------------+

+-------------------+
| UserRole |
+-------------------+
| UserID (int) (FK) | <-- Foreign key into User table
| RoleID (int) (FK) | <-- Foreign key into Role table
+-------------------+

The code is currently liberally sprinkled with calls to a method
(User.HasPermission), which takes a permission as an argument. This
method invokes a stored procedure which makes the determination. The
stored procedure can be rewritten to first get all the roles that have
the permission, and then determine whether or not the user is a member
of any of those roles. If the user is a member of any of those roles,
the method returns True.

QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?

In my company we have used several versions of similar role-based
security mechanisms and I see nothing bad in it - in fact it's quite
elegant. Remember to use permission caching. You could also find some
way of specifying the required permissions in a declarative way, so your
code will be more readable.
 
C

Carl Daniel [VC++ MVP]

Edwin said:
What part is weak?

IMO there's a level of indirection (or two) missing. Resources (pages,
bodies of code) should state the permissions they require (just like with
CAS), while roles should be used to define the set of permissions necessary
to accomplish a workflow. The mapping of permissions to roles should be
dynamic, adjustable without code or configuration changes (*). I also find
it useful to have an additional level of indirection: groups, between users
and roles. Groups can be used to represent the Personas (or "job titles")
of the users of the system, and consist of a collection of roles
(workflows). Again, the mapping between users and groups should be dynamic,
with no changes to code or configuration to move a user to a different
group.

The un-typed link between resources (code, pages, etc) and the set of valid
permissions (or roles) is a problem too - for example, there's no way to
discover that a role name was misspelled other than by running the app since
there's no "build time" checking for valid permissions.

(*) By configuration changes, I'm referring to editing of web.config or
other web-admin tasks. Ideally the configuration data is stored in a
database and can be viewed/modified by the users of the system, under
control of the very permissions, roles and groups that are being modified.

-cd
 
M

Mike Hofer

How would you do this declaratively if you don't know the name of the
role at design-time?

..neter said:
Mike said:
BACKGROUND: We've designed a Website for a client that will be
deployed across multiple physical locations. The site will be hosted
from a corporate NOC, and administered by the IT group there.

The site's end-users tend to fill each others' roles on a pretty
frequent basis. Their permissions in the system have to be extremely
flexible. We initially wanted to use role-based security, defining
concrete roles, but when we realized how these folks worked, we
realized that that simply wouldn't work. Joe would one day be out
sick and Mary would sit in his desk and do his work for him. This
happens a *lot* in this particular company, and she would have to be
able to acquire his permissions. (Not very secure, but that's what
they want.)

As a result of this, we opted to skip the roles in the code (because
they couldn't really be concretely defined), and check for the
existence of *permissions* on the user. This check occurs on each page.
We don't really look for what roles the user is in; instead, we check
to see if he has a specific permission. If he has it, access is
granted; otherwise, he's presented with the Access Denied page. (The
menu system, a custom control, prevents the users from accessing these
pages in the first place, but the pages contain fail-safe code to
prevent the users from hard-coding the URLs as well.)

PROBLEM: The IT group has recently demanded that the system implement
role-based security. This has fairly stumped us. They want to be able
to create roles in the system, apply permissions to the role, and then
place the users in the role. The system should then check to see if the
user is in the role.

The problem here, as I see it, is that the roles are then *dynamic*,
and the role-based security in .NET isn't really dynamic in nature.
When you design a Web page, you kind of have an idea of what roles you
want to access it. For instance, assume you have a form that allows you
to fill out payroll forms. Generally speaking, you only want
individuals in the PayrollEmployees role to access that page, and
that's something you know before the site is designed and deployed.
So you can write code like this:

If Not user.IsInRole("PayrollEmployees") Then
Response.Redirect("~/AccessDenied.aspx")
End If

But how do you do this when you don't know the names of the roles
beforehand?

HYPOTHESIS: Bear in mind that this stuff all has to be done within our
application's interface, and that we need to be able to manage the
permissions within the database. The application has a lookup table
with the permissions in it. We could create a series of tables like
this:

+--------------------+
| Role |
+--------------------+
| ID (int) (PK) |
| Name (varchar[255])|
+--------------------+

+-------------------------+
| RolePermission |
+-------------------------+
| RoleID (int) (FK) | <-- Foreign key into Role table
| PermissionID (int) (FK) | <-- Foreign key into Permission table
+-------------------------+

+-------------------+
| UserRole |
+-------------------+
| UserID (int) (FK) | <-- Foreign key into User table
| RoleID (int) (FK) | <-- Foreign key into Role table
+-------------------+

The code is currently liberally sprinkled with calls to a method
(User.HasPermission), which takes a permission as an argument. This
method invokes a stored procedure which makes the determination. The
stored procedure can be rewritten to first get all the roles that have
the permission, and then determine whether or not the user is a member
of any of those roles. If the user is a member of any of those roles,
the method returns True.

QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?

In my company we have used several versions of similar role-based
security mechanisms and I see nothing bad in it - in fact it's quite
elegant. Remember to use permission caching. You could also find some
way of specifying the required permissions in a declarative way, so your
code will be more readable.
 
R

Ray Booysen

A role's name isn't really important. Its only for display purposes.
You can have a set of roles (Admin, User, PowerUser) and the pages can
determine from a table of page_permissions whether the current user has
the permission. There would be no need to know the actual name of the
role. The page can just look up the permissions required for the role
and check against the roles that the user has.

Hope this helps.

Regards
Ray

Mike said:
How would you do this declaratively if you don't know the name of the
role at design-time?

.neter said:
Mike said:
BACKGROUND: We've designed a Website for a client that will be
deployed across multiple physical locations. The site will be hosted
from a corporate NOC, and administered by the IT group there.

The site's end-users tend to fill each others' roles on a pretty
frequent basis. Their permissions in the system have to be extremely
flexible. We initially wanted to use role-based security, defining
concrete roles, but when we realized how these folks worked, we
realized that that simply wouldn't work. Joe would one day be out
sick and Mary would sit in his desk and do his work for him. This
happens a *lot* in this particular company, and she would have to be
able to acquire his permissions. (Not very secure, but that's what
they want.)

As a result of this, we opted to skip the roles in the code (because
they couldn't really be concretely defined), and check for the
existence of *permissions* on the user. This check occurs on each page.
We don't really look for what roles the user is in; instead, we check
to see if he has a specific permission. If he has it, access is
granted; otherwise, he's presented with the Access Denied page. (The
menu system, a custom control, prevents the users from accessing these
pages in the first place, but the pages contain fail-safe code to
prevent the users from hard-coding the URLs as well.)

PROBLEM: The IT group has recently demanded that the system implement
role-based security. This has fairly stumped us. They want to be able
to create roles in the system, apply permissions to the role, and then
place the users in the role. The system should then check to see if the
user is in the role.

The problem here, as I see it, is that the roles are then *dynamic*,
and the role-based security in .NET isn't really dynamic in nature.
When you design a Web page, you kind of have an idea of what roles you
want to access it. For instance, assume you have a form that allows you
to fill out payroll forms. Generally speaking, you only want
individuals in the PayrollEmployees role to access that page, and
that's something you know before the site is designed and deployed.
So you can write code like this:

If Not user.IsInRole("PayrollEmployees") Then
Response.Redirect("~/AccessDenied.aspx")
End If

But how do you do this when you don't know the names of the roles
beforehand?

HYPOTHESIS: Bear in mind that this stuff all has to be done within our
application's interface, and that we need to be able to manage the
permissions within the database. The application has a lookup table
with the permissions in it. We could create a series of tables like
this:

+--------------------+
| Role |
+--------------------+
| ID (int) (PK) |
| Name (varchar[255])|
+--------------------+

+-------------------------+
| RolePermission |
+-------------------------+
| RoleID (int) (FK) | <-- Foreign key into Role table
| PermissionID (int) (FK) | <-- Foreign key into Permission table
+-------------------------+

+-------------------+
| UserRole |
+-------------------+
| UserID (int) (FK) | <-- Foreign key into User table
| RoleID (int) (FK) | <-- Foreign key into Role table
+-------------------+

The code is currently liberally sprinkled with calls to a method
(User.HasPermission), which takes a permission as an argument. This
method invokes a stored procedure which makes the determination. The
stored procedure can be rewritten to first get all the roles that have
the permission, and then determine whether or not the user is a member
of any of those roles. If the user is a member of any of those roles,
the method returns True.

QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?
In my company we have used several versions of similar role-based
security mechanisms and I see nothing bad in it - in fact it's quite
elegant. Remember to use permission caching. You could also find some
way of specifying the required permissions in a declarative way, so your
code will be more readable.
 
M

Mike Hofer

Ray said:
A role's name isn't really important. Its only for display purposes.
You can have a set of roles (Admin, User, PowerUser) and the pages can
determine from a table of page_permissions whether the current user has
the permission. There would be no need to know the actual name of the
role. The page can just look up the permissions required for the role
and check against the roles that the user has.

Hope this helps.

Regards
Ray
<--- SNIPPED -->

Maybe I'm being unclear, or I'm not getting it. The problem here is
that the IT folks want to be able to create user roles in the system
("This thing *must* use role-based security"), assign permissions to
the roles, and then place users in the roles. Declarative security
assumes that you know the names of the roles up front--before you've
built the software. That doesn't work when the names of the roles
appear out of thin air after the thing's been deployed. Does it?

Or am I missing something here?
 
M

Mike Hofer

..neter said:
Mike said:
BACKGROUND: We've designed a Website for a client that will be
deployed across multiple physical locations. The site will be hosted
from a corporate NOC, and administered by the IT group there.

The site's end-users tend to fill each others' roles on a pretty
frequent basis. Their permissions in the system have to be extremely
flexible. We initially wanted to use role-based security, defining
concrete roles, but when we realized how these folks worked, we
realized that that simply wouldn't work. Joe would one day be out
sick and Mary would sit in his desk and do his work for him. This
happens a *lot* in this particular company, and she would have to be
able to acquire his permissions. (Not very secure, but that's what
they want.)

As a result of this, we opted to skip the roles in the code (because
they couldn't really be concretely defined), and check for the
existence of *permissions* on the user. This check occurs on each page.
We don't really look for what roles the user is in; instead, we check
to see if he has a specific permission. If he has it, access is
granted; otherwise, he's presented with the Access Denied page. (The
menu system, a custom control, prevents the users from accessing these
pages in the first place, but the pages contain fail-safe code to
prevent the users from hard-coding the URLs as well.)

PROBLEM: The IT group has recently demanded that the system implement
role-based security. This has fairly stumped us. They want to be able
to create roles in the system, apply permissions to the role, and then
place the users in the role. The system should then check to see if the
user is in the role.

The problem here, as I see it, is that the roles are then *dynamic*,
and the role-based security in .NET isn't really dynamic in nature.
When you design a Web page, you kind of have an idea of what roles you
want to access it. For instance, assume you have a form that allows you
to fill out payroll forms. Generally speaking, you only want
individuals in the PayrollEmployees role to access that page, and
that's something you know before the site is designed and deployed.
So you can write code like this:

If Not user.IsInRole("PayrollEmployees") Then
Response.Redirect("~/AccessDenied.aspx")
End If

But how do you do this when you don't know the names of the roles
beforehand?

HYPOTHESIS: Bear in mind that this stuff all has to be done within our
application's interface, and that we need to be able to manage the
permissions within the database. The application has a lookup table
with the permissions in it. We could create a series of tables like
this:

+--------------------+
| Role |
+--------------------+
| ID (int) (PK) |
| Name (varchar[255])|
+--------------------+

+-------------------------+
| RolePermission |
+-------------------------+
| RoleID (int) (FK) | <-- Foreign key into Role table
| PermissionID (int) (FK) | <-- Foreign key into Permission table
+-------------------------+

+-------------------+
| UserRole |
+-------------------+
| UserID (int) (FK) | <-- Foreign key into User table
| RoleID (int) (FK) | <-- Foreign key into Role table
+-------------------+

The code is currently liberally sprinkled with calls to a method
(User.HasPermission), which takes a permission as an argument. This
method invokes a stored procedure which makes the determination. The
stored procedure can be rewritten to first get all the roles that have
the permission, and then determine whether or not the user is a member
of any of those roles. If the user is a member of any of those roles,
the method returns True.

QUESTION: Is this a viable solution? Is there a more efficient way to
do it that won't involve a major rewrite of the system? How would you
folks do this?

In my company we have used several versions of similar role-based
security mechanisms and I see nothing bad in it - in fact it's quite
elegant. Remember to use permission caching. You could also find some
way of specifying the required permissions in a declarative way, so your
code will be more readable.

In the code, we have an enumeration for each of the permissions.
Something like this:

Public Enum Permission
ViewEmployee
CreateEmployee
EditEmployee
DeleteEmployee
End Enum

Further, in the code, we have a static method on the User class that
checks for permissions, so in the code, we check for it like this:

If Not User.HasPermission(Permission.EditEmployee) Then
Navigator.GoToPermissionDeniedPage
End If

So it's already fairly declarative. Problem is, it's permission-based,
and not role-based. That's the rub with the client.
 
R

Ray Booysen

OK, fair enough. Do would have to define before hand what the
permissions are at least.

This means that you may have a Roles Table (ID primary key and a
Description) which can be added to/edited or removed at will. You'll
also have a permissions table which will hold the permissions
(RemoveCustomer, AddCustomer) and all that. A secondary table
Role_Permission will make the many to many relation work.

Another table will be the user's table. A user can have many roles and
a role can be assigned to many users. Therefore create a User_Role
table which will hold which user has which roles.

This will allow the IT dept to add users to roles, add new roles with
specific permissions with no hassle.

The interesting part will be on your side to implement the permissions.
The mapping of the permissions table to your actual code will be the
hard part.

Hope this helps

Regards
Ray
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,708
Latest member
SherleneF1

Latest Threads

Top