|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
How to Implement Dynamic Rolesdeployed 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 | The code is currently liberally sprinkled with calls to a method+-------------------+ | UserID (int) (FK) | <-- Foreign key into User table | RoleID (int) (FK) | <-- Foreign key into Role table +-------------------+ (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? Mike Hofer wrote:
> QUESTION: Is this a viable solution? Is there a more efficient way to I don't have much to offer except that having written a large role-based > do it that won't involve a major rewrite of the system? How would you > folks do this? 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 What part is weak?
Show quote "Carl Daniel [VC++ MVP]" <cpdaniel_remove_this_and_nospam@mvps.org.nospam> schreef in bericht news:%237aE2ebfGHA.764@TK2MSFTNGP05.phx.gbl... > Mike Hofer wrote: >> 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 > > Edwin Knoppert wrote:
> 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 Mike Hofer wrote:
Show quote > BACKGROUND: We've designed a Website for a client that will be In my company we have used several versions of similar role-based > 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? > 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. How would you do this declaratively if you don't know the name of the
role at design-time? ..neter wrote: Show quote > Mike Hofer wrote: > > 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. 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 Hofer wrote: Show quote > How would you do this declaratively if you don't know the name of the > role at design-time? > > .neter wrote: >> Mike Hofer wrote: >>> 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. > Ray Booysen wrote:
> A role's name isn't really important. Its only for display purposes. <--- SNIPPED -->> 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 > 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? 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 Mike Hofer wrote: Show quote > Ray Booysen wrote: >> 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? > ..neter wrote:
Show quote > Mike Hofer wrote: In the code, we have an enumeration for each of the permissions.> > 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. 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. |
|||||||||||||||||||||||