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