Hi Tauren,
If you're using a subclass of the AuthorizingRealm and caching is
enabled, the first time an AuthorizationInfo is acquired it is cached
internally and never refreshed again until the user logs out.
This happens because the
AuthorizingRealm#onLogout(PrincipalCollection) method calls
#clearCachedAuthorizationInfo(PrincipalCollection).
So if you want the system to refresh its cached values whenever you
make a change to your User authorization scheme, you need to call the
clearCachedAuthorizationInfo(principalCollection) method to ensure the
next authz check will call your doGetAuthorizationInfo implementation
again. But there is another way which is better suited for enterprise
applications, IMHO.
I myself tend not to use AuthorizingRealm or any of its subclasses
because I want data model changes to be 'immediate' and I don't want
to have to ensure this cache clearing technique is done every time I
make a data model change. So the way I ensure this is done to my
liking in all of my applications is to subclass AuthenticatingRealm
directly.
When you do this, you yourself must implement all of the authorization
methods yourself, but this extra work has a payoff: if you're using a
data model backed by an cache-enabled persistence framework like
Hibernate or JPA, you can just iterate over your data model as
required to perform the check and rely on Hibernate or JPA (or other
similar high performant OO solution) to ensure it is fast.
For example, take just one method - hasRole(PrincipalCollection
principals, String roleName):
myRealm#hasRole(PrincipalCollection userPrincipals, String roleName) {
User user = getUser(userPrincipals); //lookup your Hibernate/JPA
User domain object here
//the user.getRoles() call here relies on the persistent framework
to cache the collection
//and reset the collection when you change it in your domain model.
//therefore, every time this method is called, you'll always have
the most recent (and cached!) data:
for( Role role : user.getRoles() ) {
if ( role.getName().equals(roleName) ) {
return true;
}
}
return false;
}
You would perform similar checks based on your specific data model in
all the other remaining authorization methods.
The tradeoff here is that you have to manually implement these methods
yourself, but the big benefit is that you can change your data model
at runtime whenever you want, and you never have to 'tell' Shiro
anything - the security checks in your realm will always reflect the
most up-to-date authorization scheme because you rely on
Hibernate/JPA/other-OO-persistence-mechanism to do the lookups and
caching for you.
HTH,
Les
On Sat, Jul 18, 2009 at 8:11 PM, Tauren Mills<[email protected]> wrote:
> I have a User who has the permission "project:create". This means
> they can create new Project entities in the system.
>
> I display a list of Projects to this user. If the user has the
> "project:edit:project_id" permission, an edit button appears next to
> the project name. Same for "project:delete:project_id" with a delete
> button.
>
> So when the user creates a new project, I want to immediately add the
> permissions "project:edit:project_id" and "project:delete:project_id"
> to the user. This is so that when the page refreshes after adding a
> project, they see a revised list of projects that includes the new
> project with the edit and delete buttons. Right now, they see the
> project, but can't edit or delete it.
>
> How do I do this? I've already modified my User.permissions entities
> and persisted it to my datastore. So the next time they login, they
> will have the right permissions. I've tested logging out and back in,
> and it shows the edit and delete buttons.
>
> I first thought to look in SecurityUtils.getSubject(), but it only
> provides read methods, nothing to change the permissions.
>
> Then I thought that I should just re-authorize them somehow --
> basically get my Realm.doGetAuthorizationInfo method to run again,
> which would reload their permissions based on the User.permissions
> data in the datastore. But I'm not sure how to go about doing this.
>
> Here is my Realm code if it helps:
>
> protected AuthorizationInfo
> doGetAuthorizationInfo(PrincipalCollection principals) {
> Long memberId = (Long)
> principals.fromRealm(getName()).iterator().next();
> Member member = memberService.getMember(memberId);
> if (member != null) {
> SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
> for (Role role : member.getRoles()) {
> info.addRole(role.getName());
> for (Permission perm : role.getPermissions()) {
> info.addStringPermission(perm.getPermissionString());
> }
> }
> for (Permission perm : member.getPermissions()) {
> info.addStringPermission(perm.getPermissionString());
> }
> return info;
> } else {
> return null;
> }
> }
>
> Can anyone help? Thanks in advance!
>
> Tauren
>