Handling Nested Group Permisions (ASP.NET Role Provider) - asp.net

We have a security module which, based on group/role membership, controls permissions to resources in ASP.Net. I've built a custom ASP.Net Role Provider that queries Active Directory for group membership and which is used by this module.
Security checking works as follows for each request (caching used in places for performance reasons but excluded from this list):
Query AD for list of users group memberships
Query database for a list of users and groups with access to the requested resource
Compare results from AD with results from the database. If the user explicitly has rights or if a group that the user is in has rights then allow access, else don't.
The problem arises when we have nested groups. Lets say we have two groups: ParentGroup and ChildGroup, where ChildGroup is a member of ParentGroup in Active Directory and where our user is a member of ChildGroup. According to the logic above if we give ChildGroup access to a resource then then the user can access the resource too.
Now logically (to me anyways) if we give ParentGroup access to a resource then all members of it, and any sub groups and their members acquired recursively, should also be able to access said resource. But instead, because of the way my logic works they can't access the resource. Step 1 from the above list does not see ParentGroup it only see's ChildGroup, and Step 2 only see's ParentGroup and does not see ChildGroup.
So the question is, to make it work how I described it "Logically" should, where should I fix the problem, and is there some method that would work better then another?

Try using the WindowsPrincipal.IsInRole() method instead of querying AD directly. I posted some sample code over here which might help.

Related

Is ASP.NET role based security a true role based access control system?

From what i have read from this paper
I understand that a role based access control system is one where users can be assigned to roles where roles specify permissions to perform operations on objects
But in asp.net we do not specify "Operations on objects", what i mean here is that how can we specify "All users in Role R can perform a delete on object O"
Where is the Object Part in ASP.Net
The security model is asp.net is pretty limited. In essence you only have control at the Role level. Which means that for any operation you have to test to see if the user is any of the roles that you want to allow that operation to be performed.
We took the path of defining our own model that gives much more granularity. Basically we define operations and assign those operations to various roles. This way we can test if they have a "delete account" right versus testing if they are in "Admin", "Account Admin", or any number of other roles. It's very similar to how Active Directory works. Further it allows us to reconfigure roles as needed.
There is a piece called Authorization Manager (AzMan) that ships with windows. It can work with your membership provider to provide operation level control. Some people have had success with it, but others have complained that it's difficult to get working. We used it about 5 years ago on a project and at that time it worked about 95% of the time. The other 5% it had communications issues with our AD controller.
Which leads us to your question: Is the built in ASP.Net membership provider a true role based access control system? No. It allows you to define Roles, not operations.
Check out rhino security if you need something more fine grained.
As suggested in previous posting, to achieve more granularity you would need to build up on the existing ASP.net membership and role providers. There are third party controls such as http://www.visualaccesscontrol.com that provide role based Module Access Security and Data Access Security as well. With Visual Access Controls you can add administrative functionalities to your ASP.net web application to dynamically restrict the users to the activities they are allowed to perform and the subset of data they are allowed to see based on their respective roles.
You are implementing the delete operation, so it is up to you to check if the logged in user has permission to delete the object. For example, you might create a role "CanDeleteOs". Then, your code would look like this:
if ( !Roles.IsUserInRole("CanDeleteOs") )
throw new Exception("User does not have permission to delete O's.");

Data-based authorization in ASP.NET

Summary: I need to authorize pages based upon the data present in the query string of a url, not just the page name.
Background:
Let's say I'm building a library inventory system. Users can be created and assigned to a single library in either an Admin or User role. There are hundreds of competing libraries in the same database, so it's important to ensure that users of one library cannot view inventory from another library.
Right now I'm using a pretty standard ASP.NET setup: Forms Authentication using the SqlMembershipProvider. Authorization using the SqlRoleProvider, configured via <authorization> sections in the web.config. Security trimming with the SiteMap provider to hide unauthorized pages.
To control the inventory information from leaking, I'm manually checking a user's associate library ID with every inventory query. It works, but it's tedious and prone to errors. There has to be a better way.
Question:
Now users have the ability to create arbitrary "collections" within a library. (e.g. Collection A has Books 1, 2, & 3 in it.) Admins want the ability to grant Admin / User access on individual collections, not just the entire library.
So, if a user goes to www.com/Book.aspx?BookId=1, the system needs to ensure that user has permissions for the collection that "Book 1" is in before showing the page. If they go to www.com/Reviews.aspx?ReviewId=23, I need to make sure the Review is for a book that is in a collection that they have permission to view.
1) How can I implement this in the most standard ASP.NET way possible?
Manual checking within a base page?
A custom HttpModule?
A custom Role Provider?
I'm not interested in how to store the admin/user permissions, but rather how/where to authorize based on those permissions.
(examples on how to implement any of those are appreciated)
2) To further complicate it, I'd still like security trimming to check if the user has Admin rights on any collection or library and hide the admin pages if he doesn't.
I wouldn't handle this anywhere near the UI (ASP.NET) layer but rather within the application services. Something like:
Build services which take an IPrincipal (or your custom user object)
as a constructor parameter.
When requesting a book/review/whatever, the service is responsible
for looking to checking to see if the user has access to the
resource.
If the user doesn't have access, do some predetermined thing (pass a
message, throw an exception, return null).
This will be alot more testable and usable in the long run then worrying about it from the ASP.NET UI side.
If you have to handle it on the ASP.NET side, I'd consider using a custom IPrincipal and custom RoleProvider to wrap up each library as a role to access, then you could use most of the LoginView, etc. controls.
Normally, this sort of thing is handled at the data level. It has little to do with ASP.NET, other than ultimate you need a user-id (which comes from Membership). What you do is find some part of your entity that you want to control access on, then you create all your queries to filter on that.
For instance, if you make access at the Library level, then you would add an association between the user and the library. This can be a 1:1, 1:many, many:many, whatever your data model requires. The key is that joining through this level will always return no records, thus your entire query will return no records.
Example, assuming a user can only belong to one library.
User table has LibraryID, Books table has LibraryID, This makes an effective many-to-many join between Users and Books. So you join User and Library on LibraryID, and join Library and Books on LibraryID, then only books that belong to a library that the user is associated with will be returned by the query.
In this way, it becomes impossible for a user to query anything they are not directly associated with. The security is entirely in the database, and no business logic is required.

Roles for white-label service access

Okay,
I know I'm doing something wrong - but can't figure out a better way.
I am developing a website which is going to allow users to setup their own mini-websites.
Something like Ning.
Also, I have only 1 basic login and access to each mini website is provided (right now) via roles.
So the way I am doing this right now is:
Everytime a new mini website is created - say blah, I create 2 roles in my application.
blah_users and blah_admin
The user creating the mini website is given the role - blah_admin and every other user wanting to join this mini website (or network) is given the role - blah_user.
Anyone can view data from any website. However to add data, one must be a member of that mini site (must have the blah_user role assigned)
The problem that I am facing is that by doing a role based system, I'm having to do loads of stuff manually. Asp.Net 2 controls which work on the User.IsAunthenticated property are basically useless to me now because along with the IsAuthenticated property, I must also check if the user has the proper role.
I'm guessing there is a better way to architect the system but I am not sure how.
Any ideas?
This website is being developed in ASP.Net 2 on IIS 6.
Thanks a tonne!
I afraid standard roles-related stuff of ASP.NET is not what you need. You can try to change authentication module so it will:
Log you in with cookie.
Determine what roles does your visitor have. Perhaps you will use some special table that corresponds user and site.
Make custom principal with user roles enumerated and assign Identity and Principal to the current request.
I also don't think that making special roles for each site is good idea. When you would have hundred sites, you would also have two hundred roles. Pretty unmanageable, I afraid.
When we were solving similar task, we were just not using standard controls. We had single set of roles used on all sites. Membership of concrete user is determined according to current site and his relations to this site.
Addition: Another possibility to investigate is Application that exists in ASP.NET authentication system. Maybe it's possible to isolate each subsite into separate application?
Update: Method that works for our application.
Do not make a lot of cloned roles. Use only two: users and admin. If your sites are public then "users" role could be just global - user on one site doesn't differ from user on another site. If "users" and "everyone" are different roles, then of course "users" should also be bound to a site.
Use standard ASP.NET Membership users, but do not use standard role mechanism.
Make a mechanism for storing relation between site and user. It could be simple table that holds site id, user is and role.
What you have to override is IsInRole method. (Methods to be exact, i'll cover it later). This method is in IPrinciple interface, so you have to make your own principal object. It's quite simple.
Method IsInRole of this type should look take current site (from HttpRequest) look into the site-user table and get roles
Then you have to associate your principal with a request. Do it in PostAuthenticateRequest event.
There is also RoleProvider. Honestly I'm not sure when is it used, but it also have IsInRole method. We can override it in the same way. But other methods of this provider are harder. For example AddUsersToRoles. It accepts array of user names and roles, but to what context (site) should it be added? To current? Not sure, because I don't know when this method is called. So it requires some experiments. I see (Reflector helps) that RopePrincipal by itself uses RoleProvider to fetch list of roles, so maybe it's implement only RoleProvider, using standard principal. For our application this is not a case, so I can't say what problems could be hidden here.

Peoplesoft security

We want to give users peoplesoft portal access from outside network. But ..that access needs to be read only and very limited.
Has anyone done location based role enabling is peoplesoft.. i.e. disable all roles and enable only particular role based on ip address or web server ?
I have done something similar to that using DNS entries to determine internal/external access. The users wanted to force Self-Service access to all users when they logged into PeopleSoft if they were at home, but give the users full access if they logged in via VPN or at their desk. That way, power users would not have full security unless they were authenticated via VPN or at work.
We accomplished by customizing the USERMAINT component, created a new security table for "self service" security, adding custom Signon PeopleCode, and setting up a 2nd DNS entry that was exposed for external access.
To start, the network admin set up two sets of DNS entries that pointed to the PS web servers. For example, psoft.company.com was the internal DNS and only set for local network access, and selfservice.company.com was the external DNS available for global access.
Next, we customized the USERMAINT component (User Profile). We hid the delivered Roles tab and replaced it with a copy. The new page was pointing to a copy of PSUSERROLE. We did this because we want to use the delivered PSROLEUSER table as the "current" security table. If the Security Admin wants to update a users internal (or day-job) security, we want to store if for long term. (This will make more sense below). So the new custom table, say XXROLEUSER, is now the master table for security.
Then, we added record/page/component for the Security Admin to define "self service" security (XXSSROLES). This record/page just stores Role Names for when users log in externally. This allows the Security Admin to update self service security for seasonal changes, like Open Enrollment. The admin can add a role during Open Enrollment to grant additional access, and then remove it when the period has ended.
Last, we created a custom Signon PeopleCode step that handles the internal/external security switch. The Signon PeopleCode grabs the URL used to log in. The code parses the URL to see with DNS entry was used to access the system. If the DNS entry is "selfservice.company.com", the code will clear our the rows in PSROLEUSER for the user and insert the roles from the XXSSROLES table. If the DNS entry is "psoft.company.com", the code will clear out PSROLEUSER and insert the roles form the XXROLEUSER table.
This code change turns PSROLEUSER into a transaction table. Because the Tools system relies on this table for many different security functions, it is important to create a copy to use as the "master table" for use in the User Profile component.
If this is something you are interested in trying, I'd be happy to post some sample code for the Signon PeopleCode.
I uploaded the Signon PeopleCode function to GitHub (see link below). You can create a custom record to store the PeopleCode and then add it into the Signon PeopleCode page. If you need more information, let me know. I am not able to post the entire tech spec, but I'd be happy to help if you have questions.
http://github.com/iversond/PeopleTools-Dynamic-Login
Recruiting Solutions uses a similar process - when creating the external Candidate Gateway to allow applicants to apply for positions, a 'guest' ID is created for a particular web server that has severely restricted permissions - basically just the external applicant apply objects.
In PS 9 , have a look at PeopleTools > Web Profile > Web Profile Configuration under the security tab. You can definitely secure the system at a web server level.
HTH
Disclaimer: I work for the company.
A bit late to the party, but a useful answer: IntraSee has developed a solution for this exact situation.
The key is that you need to be able to define rules (locations) and then tie those to specific roles. Evaluated at login, the roles are revoked or granted depending on the rules and user location. It can use IP or other attributes to determine roles.
To do this manually is slightly tricky as you need to account for the versioning of the user profile and associated permissions. Signon PeopleCode is the best place to evaluate these rules as you can do it once per session, and you ahve access to the business data to make correlative decisions. E.G. if a person has a huge approval limit, maybe they can't approve remotely, but someone with a small limit can. Same security access, but add or revoked based on location and data.
To your specific case: you would have base roles with read only access to those users, then a set of 'opt-in' roles that gave read/write. When they come in from the correct location, they get the read/write. So, we are only adding the extra access when appropriate.
I would create a separate site ( like the candidate gateway suggestion ), it will need a similar web profile as the one you are currently using, no other changes would be necessary.
Next create a custom menu, then attach all of the components you wish to make read-only.
Register the content references for those custom_menu.components.
Create Roles and Permission Lists adding the custom_menu.components then select "Display Only". Then assign the roles to the users.
Expose that site via the firewall.
That's it.

secure way to authenticate administrator in ASP.NET site using OpenID with DotNetOpenID

Encouraged by SO, I'm trying to write an ASP.NET site that uses OpenID for user authentication. It's a regular WinForms site (not MVC.NET), using the DotNetOpenId library for authentication.
Is it safe for me to permit/deny administrative functions on the site by simply comparing the current session's "ClaimedID" (as returned in the OpenIdLogin_LoggedIn event, as member DotNetOpenId.RelyingParty,OpenIdEventArgs.Response.ClaimedIdentifier) to a known administrator's OpenID (i.e. mine)?
If so, is it safe for this ID to be visible (e.g. in open source code), or should it be "hidden" in a configuration file or a database row? (I know it's better design to make it configurable, my question is just about safety.)
My solution is to follow the same idea of the Roles table. After you've authenticated the user, look up that user's roles. If the user has role "Administrator" in the UserRoles table, then they can do whatever the Administrator can do.
I don't broadcast open ID's in my app. They're stored in the table. On every action result, I'm hitting the Users table, since I have also modified mine to store various user state information. With the exception of the home page, there is going to be some user information that I need from that table. I'm using LINQ, so I include the .LoadWith() to load the User with his list of roles when it serializes.
Jarrett makes some good comments about using database tables.
Just to answer another one of your questions, no, it's not a confidentiality thing to put your OpenID in your code generally. If setting up roles seems overkill for your site, a simple equality check against your ClaimedIdentifier is just perfect.

Resources