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
>>
>

Reply via email to