Hi Razvan,
You're correct in that there is a 1:1 relationship between Subject and
Session. Each Subject has 1 and only 1 Session at any given time.
But your problem might be better solved by using permissions rather than
going through all open sessions and checking them manually. JSecurity
doesn't support this directly - you'd have to create a Jira issue if you'd
like the ability to acquire all active Subjects. But this can be a
dangerous proposition, since if there are a lot of active sessions, it could
return quite a few results - not something I'm sure would be a good idea to
allow - we'd have to debate it on the dev list for sure.
But instead, let me propose using Permissions to handle your requirements.
I use them for very similar if not identical reasons in a few of my
applications:
If you perform a permission check for the area that is restricted, you can
just change the user's permission model at runtime and the next time a
permission check is executed, it can reflect the user's ability at the time
the check is performed - not necessarily their permission state of when they
logged in only.
Here's a simple example:
Lets say you restrict access to a download based on a permission. The check
might look something like this:
String specificProductDownloadPermission = "product:download:" +
awesomeProduct.getId();
if ( subject.isPermitted( specificProductDownloadPermission ) {
//show the download link
} else {
//don't show it
}
You would also execute that same Permission check again when they hit the
url that allows a download, to ensure that even if they had access to the
url at one time, that they may not have it when they visit the url.
This all implies that you can change Permissions at runtime and the User's
authorization state immediately reflects those changes. If your realm
extends JSecurity's AuthorizingRealm (
http://www.jsecurity.org/api/org/jsecurity/realm/AuthorizingRealm.html), as
most do, then by default JSecurity caches authorization info after login so
the Realm does not need to 'hit' the data store that manages the permission
relationships.
However, you need these permission checks to occur at any time during the
life of the Subject's Session, so you can't rely on this type of behavior.
You need the checks to appear instantaneous, not cached after login.
However, if you do this correctly, you don't need ot 'hit' the database even
after disabling caching after login. The way I handle this for my
applications is that I use a 2nd-level cache with Hibernate to store all my
Roles and Permission objects (for all users). This gives the code a 'feel'
that I'm checking the database every time, but in reality, I'm being saved
from that overhead by Hibernate's use of 2nd-level cache.
To support this, you would need to do 2 things:
1) Extend AuthenticatingRealm directly (not AuthorizingRealm). This
requires you to implement all the Authorization methods yourself. Because
you have to do this, then you should have 2) a domain model like the
following (based on some of my applications):
A User entity 'has a' collection of 1 or more Role objects. Each Role
object 'has a' collection of 1 or more Permission instances.
Consider a user.isPermitted() method, which would iterate over all of the
Roles assigned to it:
public boolean isPermitted( Permission permission ) {
for( Role role : getRoles() ) {
if ( role.isPermitted( permission ) {
return true;
}
}
return false;
}
The Role.isPermitted() method would iterate over its children permissions:
public boolean isPermitted( Permission permission ) {
for( Permission p : getPermissions() ) {
if ( p.implies(permission) ) {
return true;
}
}
return false;
}
Then, you Realm implementation would delegate to the User.* calls. For
example, the Realm.isPermitted method:
realm.isPermitted( PrincipalCollection userPrincipals, String permission ) {
User user = getUser( userPrincipals ); //write a method to acquire your
User instance
//create a Permission instance based on the String:
Permission perm = getPermissionResolver().resolvePermission( permission
);
return user.isPermitted( permission );
}
You would have to do this for all the Realm.* methods that aren't
implemented by AuthenticatingRealm, which would be all the authorization
methods.
You can do this iteration at runtime because of the 2nd-level cache - it is
much faster than executing a RDBMS query every time you have a permission
check. As you can see though, it _does_ require manual effort on your part
to implement these Realm methods yourself, as well as to have a domain model
that supports this.
This does however allow permission assignments (and revocations) to be
changed at runtime: you can add or remove permissions from Roles, or you
can add or remove Roles from Users, all at runtime, and without requiring
them to log out and then log back in again. I've done this for goverment
applications that require very strict control that can change at runtime,
during a user's session - the requirement is that an administrator could
change their access control at any time and the changes must be reflected
immediately.
I hope that helps! Please let me know if this makes sense. I've used it
with great success for highly dynamic environments, and assuming you can use
a 2nd-level cache, you can do this cleanly and effectively.
Best,
Les
On Tue, Feb 10, 2009 at 10:27 AM, Razvan Dragut
<[email protected]>wrote:
> Hi everyone,
>
> I am new to JSecurity, I am using it, I have a scenario to implement, I
> have some problems with it and I thought you could help :)
>
> scenario :
>
> A website that uses JSecurity to manage user's access to different parts of
> the site. Different parts of the site are subject to different terms and
> conditions. Also, the download of different products are also subject
> different terms and conditions. These terms and conditions may change while
> the users are still logged in ( rememberMe or active connection). What we
> need is that once the terms and conditions for a particular product/part of
> the site is changing, some users must be kicked before doing any action and
> forced to re-login to accept the new terms and conditions. Having this
> scenario, we need to access a list of all logged in subjects, check their
> principals against our particular set of users and kick those who match.
>
>
> problem :
>
> I've got the JSecurity sources and tried to follow code paths, debug etc
> etc and went to the point where I have all the active sessions.
> Unfortunately I haven't found a way to reach the subject associated with
> that session and I can't get the subject's principals and check them against
> my separate list.
>
>
> questions :
>
> What is the way to reach the subject via a session, in my case ?
> Is there another way/s to get all the logged in subjects ?
> Do you think is a good idea to keep a list of authenticated subjects in my
> Realm implementation ? Does it affect clustering or anything else ( mainly
> distributed stuff ) ?
>
>
> still digging :
>
> If you will point me to some listeners, none of the listeners
> (AuthenticatingListener or SessionListener) does not know about the subject
> but only auth token, auth info, and session so I cannot really handle my
> problem (storing the subject somewhere at login time or session initiation
> time) only by using listeners and without extending some JSecurity classes
> like SecurityManager impls which is not necessary a big effort but it looks
> like it (not very nice to maintain) for such a basic thing.
>
>
> Kind Regards,
>
> Thanks in advance
>
> Razvan
>
>