Just threw together an example of the sort of thing I¹m thinking
of...thoughts?  

The RelationshipPermission itself is quite hacky...but I couldn¹t find a
better way to tie into the WildcardPermission ³implies² method.  Also, I¹m
not sure if there are situations in which SecurityUtils.getSubject() isn¹t
sufficient, so you might want to pass the subject to
Relationship.satisfies(...) ­ but you¹d have to get it from there at some
point since it doesn¹t get passed to PermissionResolver ­ either that or
have your realm create RelationshipPermission objects directly.

public interface Relationship {
    boolean satisfies(String required);

    String getName();
}

public interface RelationshipSource {
    Relationship lookup(String name);
}

public class UndefinedRelationshipException extends ConfigurationException {
    public UndefinedRelationshipException(String name) {
        super(String.format("No relationship named '%s' is defined.",
name));
    }
}

public class RelationshipPermissionResolver implements PermissionResolver,
RelationshipSource {

    private Map<String, Relationship> relationships =
Collections.emptyMap();

    public RelationshipPermissionResolver() {
        this.relationships = new HashMap<String, Relationship>();
    }

    public void setRelationships(Map<String, Relationship> relationships) {
        this.relationships = relationships;
    }

    @Override
    public Permission resolvePermission(String permissionString) {
        return new RelationshipPermission(permissionString, this);
    }

    @Override
    public Relationship lookup(String name) {
        Relationship relationship = relationships.get(name);
        if(relationship == null) {
            throw new UndefinedRelationshipException(name);
        }
        return relationship;
    }
}

public class RelationshipPermission extends WildcardPermission {

    private RelationshipSource source;

    public RelationshipPermission(String wildcardString, RelationshipSource
source) {
        super(wildcardString);
        this.source = source;
    }

    public RelationshipPermission(String wildcardString, boolean
caseSensitive, RelationshipSource source) {
        super(wildcardString, caseSensitive);
        this.source = source;
    }

    @Override
    protected void setParts(String wildcardString, boolean caseSensitive) {
        super.setParts(wildcardString, caseSensitive);
        List<Set<String>> parts = this.getParts();
        for(int i = 0; i < parts.size(); i++) {
            parts.set(i, new RelationshipSet(parts.get(i)));
        }
    }

    private class RelationshipSet extends AbstractSetDecorator<String> {

        private RelationshipSet(Set<String> strings) {
            super(strings);
        }

        @Override
        public boolean containsAll(Collection<?> coll) {
            for(Object object: coll) {
                if(!contains(object)) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public boolean contains(Object object) {
            if(super.contains(object)) {
                return true;
            } else {
                if(object instanceof String) {
                    String string = (String) object;
                    for(String member: this.getSet()) {
                        if(member != null && member.startsWith("$")) {
                            Relationship relationship =
source.lookup(member.substring(1));
                            if(relationship.satisfies(string)) {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
        }
    }
}

public class ExampleRelationship implements Relationship {
    
    DocumentLookup documentLookup;
    
    @Override
    public boolean satisfies(String required) {
        Subject subject = SecurityUtils.getSubject();
        return subject != null &&
documentLookup.lookupDocumentByName(required).isOwner(subject);
    }

    @Override
    public String getName() {
        return "example";
    }
}



On 12/23/10 1:20 PM, "Jared Bunting" <[email protected]>
wrote:

> Nicolas,
> 
> The other thing that I¹ve considered doing (but haven¹t yet) is writing a
> custom Permission, and having my realm assign it to all users, that
> essentially lets me put properties in it.
> 
> So, CustomPermission ³document:delete:${ownedDocument}² would imply
> WildcardPermission ³dataStore:delete:5² iff the user owns document 5.  This
> way, admins could get ³dataStore:delete:*² and other people (say, managers)
> might get ³document:delete:${deptOwnership}².  I haven¹t worked out yet
> exactly how to define the property comparisons but they would probably point
> at some sort of global comparison singleton that accepts the subject.   One of
> my concerns is that then the permission object would be tied to a singleton ­
> not sure if that would present an issue or not.
> 
> Kalle¹s annotation seems to standardize how to do the second situation I
> mentioned before, but I¹m not sure how it would answer the other issues you
> proposed.  
> 
> I would love to get some more perspective on this, as I seem to be facing many
> of the same issues.
> 
> Thanks,
> Jared
> 
> On 12/23/10 11:33 AM, "Nicolas Antoniazzi" <[email protected]>
> wrote:
> 
>> Thanks Jared,
>> 
>> It's interresting. This was my first idea, but how to handle the fact that
>> severals users have access to the same permission on the item.
>> 1 - user that is the original creator of document can delete it (ok with your
>> code)
>> 2 - admin user should be allowed to delete the document too (so, you have to
>> add this case in the code)...
>> 3 ... Maybe other roles should have the right of deleting it too...
>> It could become a bit hard to maintain. 
>> 
>> 2010/12/23 Jared Bunting <[email protected]>
>>> I can tell you what I am currently doing to handle this situation, but I too
>>> am curious if there is a better way.
>>> 
>>> I have two slightly different situations that I am handling differently.  
>>> 
>>> In the first, we¹ll call the item ³dataStore² - there are only a few of them
>>> (but still created dynamically by the application), and users have different
>>> access to individual ones depending on if they¹re added as data consumers or
>>> data creators  (read/write).  For this, I have simply customized my realm
>>> logic to build permissions based on a separate table.  For any users in the
>>> data_store_reader table, they get the permission: dataStore:read:id1,id2,id5
>>> ­ where the ids are the names of datastores that they have access to.
>>> 
>>> In the second, we¹ll call the item ³document².  There are lots of them,
>>> they¹re created and deleted all the time.  Only the original creator has
>>> access to them.  For this situation, I have ignored the concept of
>>> permissions altogether.  In the item methods themselves (either directly, or
>>> via aop, I haven¹t decided which is best, it probably depends on the exact
>>> situation) I simply do something along the lines of:
>>> 
>>> If(!SecurityUtils.getSubject().isAuthenticated()) {
>>>   throw new UnauthenticatedException(³User must be logged in to access
>>> documents.²);
>>> } else If(!this.getOwner().equals(SecurityUtils.getSubject().getPrincipal())
>>> {
>>>   throw new UnauthorizedException(³Document ³ + this.getId() + ³ is not
>>> owned by ³ + SecurityUtils.getSubject().getPrincipal());
>>> }
>>> 
>>> I¹d be happy to further discuss any techniques and would love to hear from
>>> anyone else regarding other/better ways of approaching this sort of
>>> situation.
>>> 
>>> Thanks,
>>> Jared  
>>> 
>>> 
>>> On 12/23/10 7:39 AM, "Nicolas Antoniazzi" <[email protected]
>>> <http://[email protected]> > wrote:
>>> 
>>>> Hello,
>>>> 
>>>> I am using shiro for the server side of a gwt application. I did not find a
>>>> clear explanation in the documentation about instance permission handling.
>>>> My permissions are stored in a database. With a users_roles table and a
>>>> roles_permissions table.
>>>> 
>>>> The doc says that we can use instance level for permission
>>>> ("item:delete:13") with the WildcardPermission default system, where 13 is
>>>> the ID of the item.
>>>> 
>>>> Now, my question is : 
>>>> 1 - how to associate levels to permissions ? Do I have to store the
>>>> permission name + the level (edit, create, delete, ...) in the
>>>> roles_permissions table ?
>>>> example :
>>>> admin | item:create
>>>> admin | item:delete
>>>> admin | item:edit
>>>> 
>>>> 2 - how to set up the instance access ?
>>>> If I grant access to everything, I suppose that I could something like :
>>>> (if previous example is correct)
>>>> admin | item:create:*
>>>> admin | item:delete:*
>>>> admin | item:edit:*
>>>> 
>>>> But if I only want to grant edit access on an item to users that have
>>>> created this item, how can I do ? I suppose that there should have a method
>>>> to overload somewhere but I am a bit lost.
>>>> 
>>>> Thanks,
>>>> Nicolas.
>>>> 
>> 
>> 
> 

Reply via email to