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

Reply via email to