changeset 15:58357a4b86de

ASSIGNED - # 249: Annotations shared in groups https://it-dev.mpiwg-berlin.mpg.de/tracs/mpdl-project-software/ticket/249
author casties
date Tue, 28 Aug 2012 20:23:12 +0200
parents 629e15b345aa
children 794077e6288c
files src/main/java/de/mpiwg/itgroup/annotations/Actor.java src/main/java/de/mpiwg/itgroup/annotations/Annotation.java src/main/java/de/mpiwg/itgroup/annotations/Group.java src/main/java/de/mpiwg/itgroup/annotations/Person.java src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorGroups.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java
diffstat 10 files changed, 236 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/de/mpiwg/itgroup/annotations/Actor.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/Actor.java	Tue Aug 28 20:23:12 2012 +0200
@@ -3,6 +3,7 @@
  */
 package de.mpiwg.itgroup.annotations;
 
+import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
 import de.mpiwg.itgroup.annotations.old.NS;
 
 /**
@@ -21,19 +22,21 @@
     public abstract boolean isGroup();
 
     /**
-     * Returns if this Actor is equivalent to an Actor with this id. If this is
+     * Returns if this Actor is equivalent to Person person. If this is
      * a Group returns true when the Person is in the Group.
      * 
-     * @param userId
+     * @param person
+     * @param store AnnotationStore to check group membership
      * @return
      */
-    public boolean isEquivalentWith(String userId) {
-        if (userId == null) return false;
-        if (userId.equals(getIdString())) {
+    public boolean isEquivalentWith(Person person, AnnotationStore store) {
+        if (person == null) return false;
+        if (person.equals(getIdString())) {
             return true;
         }
-        if (isGroup()) {
-            // TODO: check if person in group
+        if (isGroup() && store != null) {
+            // check if person in group
+            return store.isPersonInGroup(person, (Group) this);            
         }
         return false;
     }
--- a/src/main/java/de/mpiwg/itgroup/annotations/Annotation.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/Annotation.java	Tue Aug 28 20:23:12 2012 +0200
@@ -3,6 +3,8 @@
  */
 package de.mpiwg.itgroup.annotations;
 
+import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
+
 /**
  * @author casties
  *
@@ -85,43 +87,44 @@
      * Returns if the requested action is allowed on this annotation.
      * 
      * @param action
-     * @param userId
+     * @param user
+     * @param store AnnotationStore to check group membership
      * @return
      */
-    public boolean isActionAllowed(String action, String userId) {
+    public boolean isActionAllowed(String action, Person user, AnnotationStore store) {
         if (action.equals("read")) {
             Actor reader = getReadPermission();
             if (reader == null) {
                 return true;
             } else {
-                return reader.isEquivalentWith(userId);
+                return reader.isEquivalentWith(user, store);
             }
         } else if (action.equals("update")) {
             // require at least an authenticated user
-            if (userId == null) return false;
+            if (user == null) return false;
             Actor updater = getUpdatePermission();
             if (updater == null) {
                 return true;
             } else {
-                return updater.isEquivalentWith(userId);
+                return updater.isEquivalentWith(user, store);
             }
         } else if (action.equals("delete")) {
             // require at least an authenticated user
-            if (userId == null) return false;
+            if (user == null) return false;
             Actor updater = getUpdatePermission();
             if (updater == null) {
                 return true;
             } else {
-                return updater.isEquivalentWith(userId);
+                return updater.isEquivalentWith(user, store);
             }
         } else if (action.equals("admin")) {
             // require at least an authenticated user
-            if (userId == null) return false;
+            if (user == null) return false;
             Actor admin = getAdminPermission();
             if (admin == null) {
                 return true;
             } else {
-                return admin.isEquivalentWith(userId);
+                return admin.isEquivalentWith(user, store);
             }
         }
         return false;
--- a/src/main/java/de/mpiwg/itgroup/annotations/Group.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/Group.java	Tue Aug 28 20:23:12 2012 +0200
@@ -20,6 +20,14 @@
         this.name = name;
     }
 
+    public Group(String id, String uri, String name) {
+        super();
+        this.id = id;
+        this.uri = uri;
+        this.name = name;
+    }
+
+
     @Override
     public boolean isGroup() {
         return true;
--- a/src/main/java/de/mpiwg/itgroup/annotations/Person.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/Person.java	Tue Aug 28 20:23:12 2012 +0200
@@ -23,6 +23,13 @@
         this.name = name;
     }
 
+    public Person(String id, String uri, String name) {
+        super();
+        this.id = id;
+        this.uri = uri;
+        this.name = name;
+    }
+
     @Override
     public boolean isGroup() {
         return false;
@@ -35,4 +42,16 @@
         return id;
     }
 
+    /**
+     * Returns a Person with this id or null.
+     * 
+     * @param id
+     * @return
+     */
+    public static Person createPersonWithId(String id) {
+        if (id != null) {
+            return new Person(id);
+        }
+        return null;
+    }
 }
--- a/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java	Tue Aug 28 20:23:12 2012 +0200
@@ -34,13 +34,13 @@
     protected GraphDatabaseService graphDb;
 
     public static enum NodeTypes {
-        ANNOTATION, PERSON, TARGET
+        ANNOTATION, PERSON, TARGET, GROUP 
     }
 
     protected List<Index<Node>> nodeIndexes;
 
     public static enum RelationTypes implements RelationshipType {
-        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ
+        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF
     }
 
     public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/";
@@ -53,6 +53,7 @@
         nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
         nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
         nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
+        nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
     }
 
     protected Index<Node> getNodeIndex(NodeTypes type) {
@@ -60,6 +61,60 @@
     }
 
     /**
+     * @param userUri
+     * @return
+     */
+    public Node getPersonNodeByUri(String userUri) {
+        if (userUri == null) return null;
+        Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle();
+        return person;
+    }
+
+    
+    /**
+     * Returns List of Groups the person is member of.
+     * 
+     * @param person
+     * @return
+     */
+    public List<Group> getGroupsForPersonNode(Node person) {
+        ArrayList<Group> groups = new ArrayList<Group>();
+        Iterable<Relationship> rels = person.getRelationships(RelationTypes.MEMBER_OF);
+        for (Relationship rel : rels) {
+            Node groupNode = rel.getEndNode();
+            Actor group = createActorFromNode(groupNode);
+            // make sure we're getting a group
+            if (!(group instanceof Group)) {
+                logger.error("target of MEMBER_OF is not GROUP! rel="+rel);
+                continue;
+            }
+            groups.add((Group) group);
+        }
+        return groups;
+    }
+    
+    /**
+     * Returns if person with uri is in Group group.
+     * 
+     * @param person
+     * @param group
+     * @return
+     */
+    public boolean isPersonInGroup(Person person, Group group) {
+        Node pn = getPersonNodeByUri(person.getUriString());
+        if (pn == null) return false;
+        // optimised version of getGroupsForPersonNode
+        Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
+        for (Relationship rel : rels) {
+            Node gn = rel.getEndNode();
+            if (gn.getProperty("uri", "").equals(group.getUriString()) || gn.getProperty("id", "").equals(group.getId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
      * Returns the Annotation with the given id.
      * 
      * @param id
@@ -142,13 +197,14 @@
      * @return
      */
     protected Actor createActorFromNode(Node actorNode) {
+        String id = (String) actorNode.getProperty("id", null);
         String uri = (String) actorNode.getProperty("uri", null);
         String name = (String) actorNode.getProperty("name", null);
         String type = (String) actorNode.getProperty("TYPE", null);
         if (type != null && type.equals("PERSON")) {
-            return new Person(uri, name);
+            return new Person(id, uri, name);
         } else if (type != null && type.equals("GROUP")) {
-            return new Group(uri, name);
+            return new Group(id, uri, name);
         }
         return null;
     }
@@ -209,7 +265,7 @@
              */
             Actor creator = annot.getCreator();
             if (creator != null) {
-                Node creatorNode = getOrCreatePersonNode(creator);
+                Node creatorNode = getOrCreateActorNode(creator);
                 getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
             }
 
@@ -300,7 +356,7 @@
         }
         if (userUri != null) {
             // there should be only one
-            Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle();
+            Node person = getPersonNodeByUri(userUri);
             if (person != null) {
                 Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
                 for (Relationship relation : relations) {
@@ -380,11 +436,17 @@
         return target;
     }
 
-    protected Node getOrCreatePersonNode(Actor actor) {
-        // Person is identified by URI
+    protected Node getOrCreateActorNode(Actor actor) {
+        // Person/Group is identified by URI or id
         String uri = actor.getUriString();
         String name = actor.getName();
-        Index<Node> idx = getNodeIndex(NodeTypes.PERSON);
+        String id = actor.getId();
+        Index<Node> idx;
+        if (actor.isGroup()) {
+            idx = getNodeIndex(NodeTypes.GROUP);
+        } else {
+            idx = getNodeIndex(NodeTypes.PERSON);
+        }
         IndexHits<Node> persons = idx.get("uri", uri);
         Node person = persons.getSingle();
         if (person == null) {
@@ -392,12 +454,19 @@
             Transaction tx = graphDb.beginTx();
             try {
                 person = graphDb.createNode();
-                person.setProperty("TYPE", NodeTypes.PERSON.name());
+                if (actor.isGroup()) {
+                    person.setProperty("TYPE", NodeTypes.GROUP.name());
+                } else {
+                    person.setProperty("TYPE", NodeTypes.PERSON.name());                    
+                }
                 person.setProperty("uri", uri);
                 idx.add(person, "uri", uri);
                 if (name != null) {
                     person.setProperty("name", name);
                 }
+                if (id != null) {
+                    person.setProperty("id", id);
+                }
                 tx.success();
             } finally {
                 tx.finish();
@@ -416,7 +485,7 @@
     protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
         Node newActorNode = null;
         if (actor != null) {
-            newActorNode = getOrCreatePersonNode(actor);
+            newActorNode = getOrCreateActorNode(actor);
         }
         Relationship rel = getRelation(annotNode, type, null);
         if (rel != null) {
@@ -463,6 +532,13 @@
         }
     }
 
+    /** returns the (first) Relationship of RelationTypes type from Node start.
+     *  
+     * @param start
+     * @param type
+     * @param direction
+     * @return
+     */
     protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
         Iterable<Relationship> rels;
         if (direction == null) {
--- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java	Tue Aug 28 20:23:12 2012 +0200
@@ -6,7 +6,6 @@
 
 import java.io.IOException;
 
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.restlet.data.Status;
@@ -18,6 +17,7 @@
 import org.restlet.resource.Put;
 
 import de.mpiwg.itgroup.annotations.Annotation;
+import de.mpiwg.itgroup.annotations.Person;
 import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
 
 /**
@@ -51,12 +51,12 @@
         // TODO: what to return without id - list of all annotations?
 
         // do authentication
-        String authUser = this.checkAuthToken(entity);
+        Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
 
         Annotation annot = getAnnotationStore().getAnnotationById(id);
         if (annot != null) {
-            if (! annot.isActionAllowed("read", authUser)) {
+            if (! annot.isActionAllowed("read", authUser, null)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
                 return null;
             }
@@ -83,7 +83,7 @@
         setCorsHeaders();
         
         // do authentication TODO: who's allowed to create? 
-        String authUser = this.checkAuthToken(entity);
+        Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
         if (authUser == null) {
             setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
@@ -141,7 +141,7 @@
         logger.debug("annotation-id=" + id);
 
         // do authentication
-        String authUser = this.checkAuthToken(entity);
+        Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
 
         Annotation annot = null;
@@ -159,7 +159,7 @@
                 setStatus(Status.CLIENT_ERROR_NOT_FOUND);
                 return null;
             }
-            if (! storedAnnot.isActionAllowed("update", authUser)) {
+            if (! storedAnnot.isActionAllowed("update", authUser, null)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN);
                 return null;
             }
@@ -204,11 +204,11 @@
         logger.debug("annotation-id=" + id);
 
         // do authentication
-        String authUser = this.checkAuthToken(entity);
+        Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
         Annotation annot = getAnnotationStore().getAnnotationById(id);
         if (annot != null) {
-            if (! annot.isActionAllowed("delete", authUser)) {
+            if (! annot.isActionAllowed("delete", authUser, null)) {
                 setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
                 return null;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorGroups.java	Tue Aug 28 20:23:12 2012 +0200
@@ -0,0 +1,84 @@
+/**
+ * ReST API for accessing groups in the Annotation store.
+ */
+package de.mpiwg.itgroup.annotations.restlet;
+
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.neo4j.graphdb.Node;
+import org.restlet.data.Form;
+import org.restlet.data.Status;
+import org.restlet.ext.json.JsonRepresentation;
+import org.restlet.representation.Representation;
+import org.restlet.resource.Get;
+
+import de.mpiwg.itgroup.annotations.Actor;
+import de.mpiwg.itgroup.annotations.Group;
+import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
+
+
+/**
+ * API for accessing groups in the Annotation store.
+ * 
+ * @author casties
+ *
+ */
+public class AnnotatorGroups extends AnnotatorResourceImpl {
+    protected String getAllowedMethodsForHeader() {
+        return "OPTIONS,GET";
+    }
+
+    /**
+     * GET with JSON content-type.
+     * Parameters: 
+     *   user: short user name
+     *   uri: user uri
+     *   
+     * @param entity
+     * @return
+     */
+    @Get("json")
+    public Representation doGetJSON(Representation entity) {
+        logger.debug("AnnotatorGroups doGetJSON!");
+        setCorsHeaders();
+        Form form = getRequest().getResourceRef().getQueryAsForm();
+        String user = form.getFirstValue("user");
+        String uri = form.getFirstValue("uri");
+        if (uri == null || uri.isEmpty()) {
+            // get uri from user-id
+            uri = Actor.getUriFromId(user, false);
+        }
+        JSONArray results = new JSONArray();
+        AnnotationStore store = getAnnotationStore();
+        Node person = store.getPersonNodeByUri(uri);
+        if (person != null) {
+            List<Group> groups = store.getGroupsForPersonNode(person);
+            for (Group group : groups) {
+                JSONObject jo = new JSONObject();
+                try {
+                    jo.put("id", group.getId());
+                    jo.put("name", group.getName());
+                    jo.put("uri", group.getUriString());
+                } catch (JSONException e) {
+                }
+                results.put(jo);
+            }
+        }
+        // assemble result object
+        JSONObject result = new JSONObject();
+        try {
+            result.put("rows", results);
+            result.put("total", results.length());
+        } catch (JSONException e) {
+            setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error");
+            return null;
+        }
+        logger.debug("sending:");
+        logger.debug(result);
+        return new JsonRepresentation(result);
+
+    }
+}
--- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java	Tue Aug 28 20:23:12 2012 +0200
@@ -532,7 +532,7 @@
         return annot;
     }
 
-    @SuppressWarnings("unused")
+    @SuppressWarnings("unused") // i in for loop
     protected Actor getActorFromPermissions(JSONArray perms) throws JSONException {
         Actor actor = null;
         for (int i = 0; i < perms.length(); ++i) {
--- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java	Tue Aug 28 20:23:12 2012 +0200
@@ -15,6 +15,8 @@
 import org.restlet.resource.Get;
 
 import de.mpiwg.itgroup.annotations.Annotation;
+import de.mpiwg.itgroup.annotations.Person;
+import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
 
 /**
  * Implements the "search" uri of the Annotator API. see
@@ -41,7 +43,7 @@
         logger.debug("AnnotatorSearch doGetJSON!");
         setCorsHeaders();
         // do authentication
-        String authUser = this.checkAuthToken(entity);
+        Person authUser = Person.createPersonWithId(this.checkAuthToken(entity));
         logger.debug("request authenticated=" + authUser);
 
         Form form = getRequest().getResourceRef().getQueryAsForm();
@@ -53,10 +55,11 @@
         JSONArray results = new JSONArray();
         // do search
         logger.debug(String.format("searching for uri=%s user=%s", uri, user));
-        List<Annotation> annots = getAnnotationStore().searchByUriUser(uri, user, limit, offset);
+        AnnotationStore store = getAnnotationStore();
+        List<Annotation> annots = store.searchByUriUser(uri, user, limit, offset);
         for (Annotation annot : annots) {
             // check permission
-            if (!annot.isActionAllowed("read", authUser)) continue;
+            if (!annot.isActionAllowed("read", authUser, store)) continue;
             JSONObject jo = createAnnotatorJson(annot, (authUser == null));
             if (jo != null) {
                 results.put(jo);
--- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java	Fri Jul 13 20:41:02 2012 +0200
+++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java	Tue Aug 28 20:23:12 2012 +0200
@@ -165,6 +165,7 @@
         router.attach("/annotator/annotations", AnnotatorAnnotations.class);
         router.attach("/annotator/annotations/{id}", AnnotatorAnnotations.class);
         router.attach("/annotator/search", AnnotatorSearch.class);
+        router.attach("/annotator/groups", AnnotatorGroups.class);
 
         // router.attach("",redirector); router.attach("/annotator",
         // ExtendedAnnotationInput.class);