Les, Thanks for the very helpful information! I just tested overriding clearCachedAuthorizationInfo in my realm to make it public and made it call super.clearCachedAuthorizationInfo. Then I inject my Realm and do the following:
realm.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); This seems to do the trick. However, I agree that implementing my own AuthenticatingRealm is probably the best long term solution and will work on doing so. Thanks again, Tauren On Mon, Jul 20, 2009 at 6:53 AM, Les Hazlewood<[email protected]> wrote: > 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 >> >
