You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by Tauren Mills <ta...@tauren.com> on 2009/07/19 02:11:42 UTC

Changing permissions at runtime

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

Re: Changing permissions at runtime

Posted by Tauren Mills <yo...@gmail.com>.
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<lh...@apache.org> 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<ta...@tauren.com> 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
>>
>

Re: Changing permissions at runtime

Posted by Les Hazlewood <lh...@apache.org>.
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<ta...@tauren.com> 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
>

Re: Changing permissions at runtime

Posted by Tcharlie <cm...@gmail.com>.
Hi,

According to what I've understood about ki, the Realm doGetAuthorization
method is called everytime you need to check an authorization.
So, I think you'll have to modify your backends permissions in order to
allow your user the "edit" action. 


Tauren Mills-3 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
> 
> 

-- 
View this message in context: http://n2.nabble.com/Changing-permissions-at-runtime-tp3282662p3287509.html
Sent from the Shiro User mailing list archive at Nabble.com.