Hello Tauren, I'll put my answers inline.
2009/7/16 Tauren Mills <[email protected]>: > I'm hoping someone can help me with some conceptual problems I'm > having on how to best use shiro roles and permissions. As an example, > assume I have the following entities: > > User > String username > Set<Project> projects > > Project > String name > Set<User> users; > > Maybe the following roles are available: > Superuser -- gives full read/write access to all projects > Administrator -- gives full read/write access to a project's features > Manager -- gives read/write access to most features, read only to a few > Scheduler -- read/write only for scheduling resources (rooms, equipment, > etc.) > Member -- project team member can read many things and write few > Watcher -- can read some info about project, but can't change it. > Watcher can't see member list. > > A user can belong to multiple projects, and can have different > roles/permissions in each project. > > A user can have multiple roles in the same project (i.e. can be both a > member and a scheduler). > > So it seems that I mainly need to use permissions, not roles. So that > I can do something like: > > if ( currentUser.isPermitted( "project:schedule:world_domination" ) ) { > log.info("You are permitted to 'schedule' the 'project' with name > (id) 'world_domination'. "); > } else { > log.info("Sorry, you aren't allowed to 'schedule' the > 'world_domination' 'project'!"); > } The code looks fine in general. There are 2 things I notice: 1) isPermitted is a method of the Subject interface. Your variable naming implies that you combine your User class with Shiro's interface. This may or may not be a good idea. Depends on your use case, I guess. The point at issue is that you may be better of not to couple your application/business logic to the security framework. 2) I personally dislike the if / else style that this snippet would presumably grow into. I'd recommend to check with negative logic and quit early. This saves a level of indentation and more important one layer of cognitive context to manage: void scheduleProject(String projectId) { Subject subject = SecurityUtils.getSubject(); if (!suject.isPermitted("project:schedule:world_domination")) throw new NoPermissionException(); // TODO: do the work } > So given this, I would need entities like this: > > User > String username > Set<Project> projects > Set<Role> roles > Set<String> permissions > > Project > String name > Set<User> users; > > Role > String name > Set<String> permissions Looks OK, but again, it doesn't need to be modeled this way. Shiro will ask your realms for the authentication and authorization information. It doesn't care, what your application/business objects look like. If your realms need these objects, to manage the access privilege provisioning, that's a totally different story. > However, it doesn't really seem like I'd be using the Role feature > much. Is this correct? It seems like instead User.permissions would > be full of values like: > project.admin.build_fort > project.schedule.world_domination > project.member.world_domination > project.watch.paint_house I have the feeling, that you might not have fully understood that there are two aspects to the whole functionality: 1) Your code performing security checks 2) Any tool (could be your code) managing the access privilege landscape (permissions, roles, etc.) re 1) In general, your code would only check for permissions, not roles. No calls like subject.hasRole("admin"), only calls to subject.isPermitted("project:createNew") etc. Plain functionality and permissions match 1:1, while functionality and roles match n:1. So, with your application coded against roles, a) every time you add functionality you change the meaning of the role. But what's even worse is that b) if security needs change (managers shall no longer be able to xyz), you'll need to modify your code (design-time change). If your application is coded against permissions, the permission check is still the same, only the mapping who or which role effectively has this permission will need to change (run-time change). But this does not mean that you have to store the exact permission with the user (re 2). E.g. if you develop an application for a large enterprise, you'd rather want an IDM solutioin to do the privilege management and provisioning, e.g. into a corporate LDAP system. > I can see using Roles for things like: > -- display a Schedule Projects button if I'm a member of the Scheduler role. > -- note that I should see this button if I have either Scheduler or > Admin role for any project As stated above: Don't. Use a permission like "project:schedule:the_project_id" and assign this permission to both roles (dynamically) in the privilege management system. > But what I'm getting confused on is how to integrate this with > Hibernate queries. For instance: > > select list of projects where I have any role > -- query should return all projects listed in the 3rd term of any > permission I have, where the first term is 'project' > > select list of projects where I am a scheduler or a manager > -- query should return all projects listed in the 3rd term of any > permission I have where the 2nd term is 'schedule' or 'manage' and the > first term is 'project' > > select list of users that belong to project world_domination > -- query should return all users with any permission that contains > 'world_domination' in the 3rd term and 'project' in the 1st term. > -- however, the query needs to also make sure that I have a > permission with 'world_domination' in the 3rd term, 'project' in the > 1st term, and that I have a value other/greater than 'watch' in the > 2nd term (since watchers can't see a project's members) > > select list of users that belong to project paint_house > -- same as previous, but this time I should get no results since I > am a watcher of paint_house, so can't see members I see. The problem here is, that some of your application logic implies access privileges. I'd store e.g. the project manager in the application model, and take care for some work flow to always change the application model's data with the security realms' data in a transactional way. All of this is very much dependant on how your application and data model will develop. You could e.g. just have a n:m mapping of users and projects named "involvedInProjects". > Should I be using a Permission entity instead of a string? Will Shiro > support this? This way it's easier to build hibernate queries and > mappings. Take for instance: Actually, Shiro is built around Permission instances. But ever since the mighty WildcardPermission got introduced, it prooved so flexible, that it became the default and it's very rare that you need anything else. > User > String username > Set<Permission> permissions; > > Permission > String entityType; > String roleName; > String instanceName; > > But now I'm starting to feel like I won't even be using Shiro. I'm > basically building permissions and roles into my application. I mean, > with this design, I could get a list of all of permitted entities just > via a hibernate query. I wouldn't need to make a shiro call. Thus I'm > confused. My first shot would be to have a single "is involved in" relation mapping n users to m projects. You can then query for "all projects, the current user is involved in". Then iterate over this collection and check if the current user has the permission to do something about it. > Can someone tell me if I'm on the right track here? Or should I be > looking at a different design? To sum up my advices: Separate your application logic and model from the security management. Ask yourself: Does the application really need to know who has which roles/permissions? Isn't it enough, if it can rely that certain actions will be prohibited if the issuer of the action is not privileged to do so? I hope I could help you to sort things out a bit. Note though, that I'm not a Shiro expert. It's just my personal thoughts, based on my programming experience and (limited) understanding of Shiro. Cheers, DJ
