Hi Daniel,
It's good to hear you thinking that I'm pretty much on track. See
inline below...
.
>> class Member
>> Set<Role> roles;
>> Set<ProjectParticipant> participations;
>>
>> class Role
>> String name
>> Set<Permission> permissions;
>>
>> class ProjectParticipant
>> boolean owner;
>> boolean admin;
>> boolean scheduler;
>
> Maybe those classes need a little more polish. Depends on what they
> are supposed to do. For my taste they are a little too close to the
> physical design, i.e. how they are stored in the DB. But if you only
> use them as DTOs that's absolutely OK, because that's the purpose of
> the DTO.
> (The fact that you referred to those classes as "entities" makes me
> believe they are very close to the physical layout what is stored in
> the DB.)
Perhaps you could explain what you mean by being too close to the
physical design? These are my (simplified) Hibernate classes,
annotated with @Entity. They are what ultimately gets stored in the
database, each one having their own table. Plus there are additional
relationship tables created by Hibernate. So yes, I guess they are the
physical design. But I designed them like this since because they have
the fields and relationships that my business logic needs.
Can you give an example of how they would look if they were NOT too
close to the physical design? I'm not really using any DTOs, simply
passing my hibernate entities from the service layer to the UI (which
is done with Wicket). For my project, it seemed overkill to use DTOs,
although there have been a couple instances where I wondered if my
life would be easier if I had.
Also, are you suggesting that my Shiro realm code should be creating
the permissions from data in DTOs? Or should it be using the hibernate
entities directly, as I'm doing now?
Those example classes I provided had ben drastically simplified and
left a lot of relationships out. I case it helps, below are more
detailed hibernate entities with additional relationship information.
Member and Role is the same as before:
class ProjectParticipant
Member member;
Project project;
OwnerParticipationDetails owner;
AdminParticipationDetails admin;
SchedulerParticipationDetails scheduler;
LeaderParticipationDetails leader;
TeammateParticipationDetails teammate;
class SchedulerParticipationDetails
Certificate certificate;
Rating rating;
class OwnerParticipationDetails
class AdminParticipationDetails
class LeaderParticipationDetails
class TeammateParticipationDetails
class Project
Set<Location> locations;
Set<Task> tasks;
Set<ProjectParticipant> participants;
So, this allows a member to be a participant in multiple projects. For
each project, they can take on different roles (Owner, Admin,
Scheduler, Leader, Teammate). Additional information can be stored for
type of participation that a member has in a project (such as a
Certificate and a Rating for members who are schedulers). I haven't
defined any extra information yet for most roles, and simply test if
the *ParticipationDetails is null or not. But this allows me to easily
extend things when I discover some data that needs to be stored for a
particular type of participant. A Task can be assigned a Location from
the set of Project.locations, but I'll leave off other Task
information as it isn't really important here.
> This actually looks very good to me. This way in your business logic
> you can check for
> currentSubject.hasPermission("project:edit:17") // [1]
This is exactly what I'm doing. Since my Realm creates permissions
based on my data, I'm able to just use Shiro to find permissions.
> instead of (I make this call up)
> member.getParticipationForProject(project).isAdmin() // [2]
>
> Brilliant! I haven't thought about that at all. Kudos to you!
Thanks! I'm glad this solution looks good to you, as I'm very happy with it!
> So, as in call [1] your logic is nicely decoupled from the (more)
> physical design, the classes from above are absolutely fine.
>
> A last note: You surely know that with Shiro you can use several
> realms simultaneously. Usually you have one realm per media/technology
> (e.g. DB, LDAP...). An idea just popped up in my head. I don't know if
> that is neccessarily a good idea (some more experienced developer
> wants to chime in here?), but you could separate the "project
> participation" related authorization into one realm and the general
> role/permission based authorization into another realm.
> The "project participation" implied permissions are tightly coupled to
> business logic anyway. It will have to change if your security
> implications governed by business logic change, e.g. if some day
> admins are allowed to schedule, too. But if you separate this into its
> own realm, you'd only have to touch this realm's code.
Yes, I am aware of Shiro having multiple realms. And maybe someday
I'll need to go there. But for now, I want to keep things simple. If I
need to add other media/technology, such as LDAP, etc in the future,
creating a new realm for it is exactly what I'll do.
>> Of course, there is still more to be done. For instance, I can see
>> how I could remove the Role entity altogether as well and let the
>> Shiro roles be determined by the business logic.
>
> I think what you mean is what I tried to explain with the two parts
> above. I am quite sure you're on the right track. You might want to
> add an additional layer above those classes you presented earlier, to
> remove the last bits of physical design that you don't need for your
> business logic.
Again, I'm unclear what you mean with the additional layer above my
classes, "to remove the last bits of physical design". Could you
explain, or show a simple example (doesn't have to be my model if
something else is easier to illustrate)?
I'm certainly on track now, so feel free to drop things now if you
don't have time to respond. If you do have time, I would appreciate
some more clarification on your references to further abstraction from
the physical design.
Thanks again for your help!
Tauren