You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by ro...@apache.org on 2014/11/13 02:09:13 UTC

[5/8] incubator-usergrid git commit: organized rest IT

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/groups/GroupResourceIT.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/groups/GroupResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/groups/GroupResourceIT.java
new file mode 100644
index 0000000..0dd6dc8
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/groups/GroupResourceIT.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.groups;
+
+
+import java.util.UUID;
+
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.usergrid.cassandra.Concurrent;
+import org.apache.usergrid.java.client.Client.Query;
+import org.apache.usergrid.java.client.response.ApiResponse;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.utils.UUIDUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+
+/** @author tnine */
+@Concurrent()
+public class GroupResourceIT extends AbstractRestIT {
+    private static Logger log = LoggerFactory.getLogger( GroupResourceIT.class );
+
+    private static final String GROUP = "testGroup";
+
+    private static final String USER = "edanuff";
+
+    private static boolean groupCreated = false;
+
+
+    public GroupResourceIT() throws Exception {
+
+    }
+
+
+    @Before
+    public void setupGroup() {
+        if ( groupCreated ) {
+            return;
+        }
+
+        try {
+            client.createGroup( GROUP );
+            groupCreated = true;
+        }
+        catch ( Exception e ) {
+            log.error( "Error creating group " + GROUP, e );
+        }
+        refreshIndex("test-organization", "test-app");
+
+    }
+
+
+    @Test
+    public void failGroupNameValidation() {
+
+        ApiResponse response = client.createGroup( "groupName/withslash" );
+        assertNull( response.getError() );
+
+        refreshIndex("test-organization", "test-app");
+
+        {
+            boolean failed = false;
+            try {
+                ApiResponse groupResponse = client.createGroup( "groupName withspace" );
+                failed = groupResponse.getError() != null;
+            } catch ( Exception e ) {
+                failed = true;
+            }
+            assertTrue( failed );
+        }
+    }
+
+
+    @Test
+    public void postGroupActivity() {
+
+        // don't populate the user, it will use the currently authenticated
+        // user.
+
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String groupPath = "groupPath" + id;
+        String groupTitle = "groupTitle " + id;
+        String groupName = "groupName" + id;
+
+        ApiResponse response = client.createGroup( groupPath, groupTitle, groupName );
+
+        assertNull( "Error was: " + response.getErrorDescription(), response.getError() );
+
+        refreshIndex("test-organization", "test-app");
+
+        UUID newId = response.getEntities().get( 0 ).getUuid();
+
+        Query results = client.queryGroups( String.format( "name='%s'", groupName ) );
+
+        response = results.getResponse();
+
+        UUID entityId = response.getEntities().get( 0 ).getUuid();
+
+        assertEquals( newId, entityId );
+
+        results = client.queryGroups( String.format( "title='%s'", groupTitle ) );
+
+        response = results.getResponse();
+
+        entityId = response.getEntities().get( 0 ).getUuid();
+
+        assertEquals( newId, entityId );
+
+        results = client.queryGroups( String.format( "title contains '%s'", id ) );
+
+        response = results.getResponse();
+
+        entityId = response.getEntities().get( 0 ).getUuid();
+
+        assertEquals( newId, entityId );
+
+        results = client.queryGroups( String.format( "path='%s'", groupPath ) );
+
+        response = results.getResponse();
+
+        entityId = response.getEntities().get( 0 ).getUuid();
+
+        assertEquals( newId, entityId );
+    }
+
+
+    @Test
+    public void addRemovePermission() throws IOException {
+
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String groupName = "groupname" + id;
+
+        ApiResponse response = client.createGroup( groupName );
+        assertNull( "Error was: " + response.getErrorDescription(), response.getError() );
+
+        refreshIndex("test-organization", "test-app");
+
+        UUID createdId = response.getEntities().get( 0 ).getUuid();
+
+        // add Permission
+
+        String json = "{\"permission\":\"delete:/test\"}";
+        JsonNode node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/permissions" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, json ));
+
+        // check it
+        assertNull( node.get( "errors" ) );
+        assertEquals( node.get( "data" ).get( 0 ).asText(), "delete:/test" );
+
+        refreshIndex("test-organization", "test-app");
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/permissions" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertEquals( node.get( "data" ).get( 0 ).asText(), "delete:/test" );
+
+
+        // remove Permission
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/permissions" )
+                .queryParam( "access_token", access_token ).queryParam( "permission", "delete%3A%2Ftest" )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+
+        // check it
+        assertNull( node.get( "errors" ) );
+        assertTrue( node.get( "data" ).size() == 0 );
+
+        refreshIndex("test-organization", "test-app");
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/permissions" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertTrue( node.get( "data" ).size() == 0 );
+    }
+
+
+    @Test
+    public void addRemoveRole() throws IOException {
+
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String groupName = "groupname" + id;
+        String roleName = "rolename" + id;
+
+        ApiResponse response = client.createGroup( groupName );
+        assertNull( "Error was: " + response.getErrorDescription(), response.getError() );
+
+        UUID createdId = response.getEntities().get( 0 ).getUuid();
+
+        refreshIndex("test-organization", "test-app");
+
+        // create Role
+
+        String json = "{\"title\":\"" + roleName + "\",\"name\":\"" + roleName + "\"}";
+        JsonNode node = mapper.readTree( resource().path( "/test-organization/test-app/roles" ).queryParam( "access_token", access_token )
+                        .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                        .post( String.class, json ));
+
+        // check it
+        assertNull( node.get( "errors" ) );
+
+
+        refreshIndex("test-organization", "test-app");
+
+        // add Role
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/roles/" + roleName )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        refreshIndex("test-organization", "test-app");
+
+        // check it
+        assertNull( node.get( "errors" ) );
+        assertEquals( node.get( "entities" ).get( 0 ).get( "name" ).asText(), roleName );
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/roles" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertEquals( node.get( "entities" ).get( 0 ).get( "name" ).asText(), roleName );
+
+        // check root roles
+        node = mapper.readTree( resource().path( "/test-organization/test-app/roles" ).queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertTrue( node.get( "entities" ).findValuesAsText( "name" ).contains( roleName ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        // remove Role
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/roles/" + roleName )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+        assertNull( node.get( "errors" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/groups/" + createdId + "/roles" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertTrue( node.get( "entities" ).size() == 0 );
+
+        // check root roles - role should remain
+        node = mapper.readTree( resource().path( "/test-organization/test-app/roles" ).queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertTrue( node.get( "entities" ).findValuesAsText( "name" ).contains( roleName ) );
+
+        // now kill the root role
+        node = mapper.readTree( resource().path( "/test-organization/test-app/roles/" + roleName )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+        assertNull( node.get( "errors" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        // now it should be gone
+        node = mapper.readTree( resource().path( "/test-organization/test-app/roles" ).queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        assertNull( node.get( "errors" ) );
+        assertFalse( node.get( "entities" ).findValuesAsText( "name" ).contains( roleName ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingEntitiesTest.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingEntitiesTest.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingEntitiesTest.java
new file mode 100644
index 0000000..93a061d
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingEntitiesTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.paging;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.rest.TestContextSetup;
+import org.apache.usergrid.rest.test.resource.CustomCollection;
+
+import org.apache.commons.lang.ArrayUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+
+
+/**
+ * // TODO: Document this
+ *
+ * @author ApigeeCorporation
+ * @since 4.0
+ */
+public class PagingEntitiesTest extends AbstractRestIT {
+
+    @Rule
+    public TestContextSetup context = new TestContextSetup( this );
+
+
+    @Test //USERGRID-266
+    public void pageThroughConnectedEntities() throws IOException {
+
+        CustomCollection activities = context.collection( "activities" );
+
+        long created = 0;
+        int maxSize = 100;
+        long[] verifyCreated = new long[maxSize];
+        Map actor = hashMap( "displayName", "Erin" );
+        Map props = new HashMap();
+
+
+        props.put( "actor", actor );
+        props.put( "verb", "go" );
+
+        for ( int i = 0; i < maxSize; i++ ) {
+
+            props.put( "ordinal", i );
+            JsonNode activity = activities.create( props );
+            verifyCreated[i] = activity.findValue( "created" ).longValue();
+            if ( i == 0 ) {
+                created = activity.findValue( "created" ).longValue();
+            }
+        }
+        ArrayUtils.reverse( verifyCreated );
+        
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String query = "select * where created >= " + created;
+
+
+        JsonNode node = activities.query( query, "limit", "2" ); //activities.query(query,"");
+        int index = 0;
+        while ( node.get( "entities" ).get( "created" ) != null ) {
+            assertEquals( 2, node.get( "entities" ).size() );
+
+            if ( node.get( "cursor" ) != null ) {
+                node = activities.query( query, "cursor", node.get( "cursor" ).toString() );
+            }
+
+            else {
+                break;
+            }
+        }
+    }
+
+
+    @Test //USERGRID-1253
+    public void pagingQueryReturnCorrectResults() throws Exception {
+
+        CustomCollection activities = context.collection( "activities" );
+
+        long created = 0;
+        int maxSize = 23;
+        long[] verifyCreated = new long[maxSize];
+        Map actor = hashMap( "displayName", "Erin" );
+        Map props = new HashMap();
+
+        props.put( "actor", actor );
+        props.put( "content", "bragh" );
+
+        for ( int i = 0; i < maxSize; i++ ) {
+
+            if ( i > 17 && i < 23 ) {
+                props.put( "verb", "stop" );
+            }
+            else {
+                props.put( "verb", "go" );
+            }
+            props.put( "ordinal", i );
+            JsonNode activity = activities.create( props );
+            verifyCreated[i] = activity.findValue( "created" ).longValue();
+            if ( i == 18 ) {
+                created = activity.findValue( "created" ).longValue();
+            }
+        }
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String query = "select * where created >= " + created + " or verb = 'stop'";
+
+        JsonNode node = activities.withQuery( query ).get();
+
+        for ( int index = 0; index < 5; index++ ) {
+            assertEquals( verifyCreated[maxSize - 1 - index],
+                    node.get( "entities" ).get( index ).get( "created" ).longValue() );
+        }
+
+        int totalEntitiesContained = activities.countEntities( query );
+
+        assertEquals( 5, totalEntitiesContained );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingResourceIT.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingResourceIT.java
new file mode 100644
index 0000000..397dd0f
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/paging/PagingResourceIT.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.paging;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.usergrid.cassandra.Concurrent;
+import org.apache.usergrid.java.client.entities.Entity;
+import org.apache.usergrid.java.client.response.ApiResponse;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.rest.TestContextSetup;
+import org.apache.usergrid.rest.test.resource.CustomCollection;
+import org.apache.usergrid.rest.test.resource.EntityResource;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/** Simple tests to test querying at the REST tier */
+@Concurrent()
+public class PagingResourceIT extends AbstractRestIT {
+
+    private static final Logger logger = LoggerFactory.getLogger( PagingResourceIT.class );
+
+    @Rule
+    public TestContextSetup context = new TestContextSetup( this );
+
+
+    @Test
+    public void collectionPaging() throws Exception {
+
+        CustomCollection things = context.application().collection( "test1things" );
+
+        int size = 40;
+
+        List<Map<String, String>> created = new ArrayList<Map<String, String>>( size );
+
+        for ( int i = 0; i < size; i++ ) {
+            Map<String, String> entity = hashMap( "name", String.valueOf( i ) );
+            things.create( entity );
+
+            created.add( entity );
+        }
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // now page them all
+        ApiResponse response = null;
+        Iterator<Map<String, String>> entityItr = created.iterator();
+
+        do {
+
+            response = parse( things.get() );
+
+            for ( Entity e : response.getEntities() ) {
+                assertTrue( entityItr.hasNext() );
+                assertEquals( entityItr.next().get( "name" ), e.getProperties().get( "name" ).asText() );
+                logger.debug("Got item value {}", e.getProperties().get( "name" ).asText());
+            }
+
+            logger.debug("response cursor: " + response.getCursor() );
+            
+            things = things.withCursor( response.getCursor() );
+        }
+        while ( response != null && response.getCursor() != null );
+
+        assertFalse("Should have paged them all", entityItr.hasNext() );
+    }
+
+
+    @Test
+    @Ignore("ignored because currently startPaging is only be supported for queues and not for  "
+            + "generic collections as this test assumes. "
+            + "see also: https://issues.apache.org/jira/browse/USERGRID-211 ")
+    public void startPaging() throws Exception {
+
+        CustomCollection things = context.application().collection( "test2things" );
+
+        int size = 40;
+
+        List<Map<String, String>> created = new ArrayList<Map<String, String>>( size );
+
+        for ( int i = 0; i < size; i++ ) {
+            Map<String, String> entity = hashMap( "name", String.valueOf( i ) );
+            things.create( entity );
+
+            created.add( entity );
+        }
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // now page them all
+        ApiResponse response = null;
+
+        UUID start = null;
+        int index = 0;
+
+        do {
+
+            response = parse( things.get() );
+
+            for ( Entity e : response.getEntities() ) {
+                logger.debug("Getting item {} value {}", index, e.getProperties().get( "name" ).asText());
+                assertEquals( created.get( index ).get( "name" ), e.getProperties().get( "name" ).asText() );
+                index++;
+            }
+
+            // decrement since we'll get this one again
+            index--;
+
+            start = response.getEntities().get( response.getEntities().size() - 1 ).getUuid();
+
+            things = things.withStart( start );
+        }
+        while ( response != null && response.getEntities().size() > 1 );
+
+        // we paged them all
+        assertEquals( created.size() - 1, index );
+    }
+
+
+    @Test
+    public void collectionBatchDeleting() throws Exception {
+
+        CustomCollection things = context.application().collection( "test3things" );
+
+        int size = 40;
+
+        List<Map<String, String>> created = new ArrayList<Map<String, String>>( size );
+
+        for ( int i = 0; i < size; i++ ) {
+            Map<String, String> entity = hashMap( "name", String.valueOf( i ) );
+            things.create( entity );
+
+            created.add( entity );
+        }
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        ApiResponse response;
+        int deletePageSize = 10;
+
+        things = things.withLimit( deletePageSize );
+
+        for ( int i = 0; i < size / deletePageSize; i++ ) {
+            response = parse( things.delete() );
+
+            refreshIndex(context.getOrgName(), context.getAppName());
+
+            assertEquals( "Only 10 entities should have been deleted", 10, response.getEntityCount() );
+        }
+
+        response = parse( things.get() );
+
+        assertEquals( "All entities should have been removed", 0, response.getEntityCount() );
+
+        //now do 1 more delete, we should get any results
+
+        response = parse( things.delete() );
+
+        assertEquals( "No more entities deleted", 0, response.getEntityCount() );
+    }
+
+
+    @Test
+    public void emptyQlandLimitIgnored() throws Exception {
+
+        CustomCollection things = context.application().collection( "test4things" );
+
+        Map<String, String> data = hashMap( "name", "thing1" );
+        JsonNode response = things.create( data );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        JsonNode entity = getEntity( response, 0 );
+
+        String uuid = entity.get( "uuid" ).asText();
+
+        EntityResource entityRequest = things.entity( "thing1" ).withParam( "ql", "" ).withParam( "limit", "" );
+
+        JsonNode returnedEntity = getEntity( entityRequest.get(), 0 );
+
+        assertEquals( entity, returnedEntity );
+
+        entityRequest = things.entity( uuid ).withParam( "ql", "" ).withParam( "limit", "" );
+
+        returnedEntity = getEntity( entityRequest.get(), 0 );
+
+        assertEquals( entity, returnedEntity );
+
+        // now do a delete
+        returnedEntity = getEntity( entityRequest.delete(), 0 );
+
+        assertEquals( entity, returnedEntity );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // verify it's gone
+        returnedEntity = getEntity( things.entity( uuid ).get(), 0 );
+
+        assertNull( returnedEntity );
+    }
+
+
+    private static ObjectMapper mapper = new ObjectMapper();
+
+
+    private static final ApiResponse parse( JsonNode response ) throws Exception {
+        String jsonResponseString = mapper.writeValueAsString( response );
+        return mapper.readValue( jsonResponseString, ApiResponse.class );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java
new file mode 100644
index 0000000..e034443
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/ConnectionResourceTest.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.users;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.junit.Rule;
+import org.junit.Test;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.rest.TestContextSetup;
+import org.apache.usergrid.rest.test.resource.CustomCollection;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import java.io.IOException;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+
+
+/**
+ * // TODO: Document this
+ *
+ * @author ApigeeCorporation
+ * @since 4.0
+ */
+public class ConnectionResourceTest extends AbstractRestIT {
+    @Rule
+    public TestContextSetup context = new TestContextSetup( this );
+
+
+    @Test
+    public void connectionsQueryTest() throws IOException {
+
+
+        CustomCollection activities = context.collection( "peeps" );
+
+        Map stuff = hashMap( "type", "chicken" );
+
+        activities.create( stuff );
+
+
+        Map<String, Object> payload = new LinkedHashMap<String, Object>();
+        payload.put( "username", "todd" );
+
+        Map<String, Object> objectOfDesire = new LinkedHashMap<String, Object>();
+        objectOfDesire.put( "codingmunchies", "doritoes" );
+
+        resource().path( "/test-organization/test-app/users" ).queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                .post( String.class, payload );
+
+        payload.put( "username", "scott" );
+
+
+        resource().path( "/test-organization/test-app/users" ).queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                .post( String.class, payload );
+    /*finish setting up the two users */
+
+
+        refreshIndex("test-organization", "test-app");
+
+        ClientResponse toddWant = resource().path( "/test-organization/test-app/users/todd/likes/peeps" )
+                .queryParam( "access_token", access_token ).accept( MediaType.TEXT_HTML )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( ClientResponse.class, objectOfDesire );
+
+        assertEquals( 200, toddWant.getStatus() );
+
+        refreshIndex("test-organization", "test-app");
+
+        JsonNode node = mapper.readTree( resource().path( "/test-organization/test-app/peeps" ).queryParam( "access_token", access_token )
+                        .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                        .get( String.class ));
+
+        String uuid = node.get( "entities" ).get( 0 ).get( "uuid" ).textValue();
+
+
+        try {
+            node = mapper.readTree( resource().path( "/test-organization/test-app/users/scott/likes/" + uuid )
+                    .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+            assert ( false );
+        }
+        catch ( UniformInterfaceException uie ) {
+            assertEquals( 404, uie.getResponse().getClientResponseStatus().getStatusCode() );
+        }
+    }
+
+
+    @Test
+    public void connectionsLoopbackTest() throws IOException {
+
+        CustomCollection things = context.collection( "things" );
+
+        UUID thing1Id = getEntityId( things.create( hashMap( "name", "thing1" ) ), 0 );
+
+        UUID thing2Id = getEntityId( things.create( hashMap( "name", "thing2" ) ), 0 );
+
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //create the connection
+        things.entity( thing1Id ).connection( "likes" ).entity( thing2Id ).post();
+
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //test we have the "likes" in our connection meta data response
+
+        JsonNode response = things.entity( "thing1" ).get();
+
+        String url = getEntity( response, 0 ).get( "metadata" ).get( "connections" ).get( "likes" ).asText();
+
+
+        assertNotNull( "Connection url returned in entity", url );
+
+        //trim off the start /
+        url = url.substring( 1 );
+
+
+        //now that we know the URl is correct, follow it
+
+        response = context.collection( url ).get();
+
+        UUID returnedUUID = getEntityId( response, 0 );
+
+        assertEquals( thing2Id, returnedUUID );
+
+
+        //now follow the loopback, which should be pointers to the other entity
+
+        url = getEntity( response, 0 ).get( "metadata" ).get( "connecting" ).get( "likes" ).asText();
+
+        assertNotNull( "Incoming edge URL provited", url );
+
+        //trim off the start /
+        url = url.substring( 1 );
+
+        //now we should get thing1 from the loopback url
+
+        response = context.collection( url ).get();
+
+        UUID returned = getEntityId( response, 0 );
+
+        assertEquals( "Should point to thing1 as an incoming entity connection", thing1Id, returned );
+    }
+
+
+    @Test
+    public void connectionsUUIDTest() throws IOException {
+
+        CustomCollection things = context.collection( "things" );
+
+        UUID thing1Id = getEntityId( things.create( hashMap( "name", "thing1" ) ), 0 );
+
+        UUID thing2Id = getEntityId( things.create( hashMap( "name", "thing2" ) ), 0 );
+
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //create the connection
+        things.entity( thing1Id ).connection( "likes" ).entity( thing2Id ).post();
+
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //test we have the "likes" in our connection meta data response
+
+        JsonNode response = things.entity( "thing1" ).get();
+
+        String url = getEntity( response, 0 ).get( "metadata" ).get( "connections" ).get( "likes" ).asText();
+
+
+        assertNotNull( "Connection url returned in entity", url );
+
+        //trim off the start /
+        url = url.substring( 1 );
+
+
+        //now that we know the URl is correct, follow it
+
+        response = context.collection( url ).get();
+
+        UUID returnedUUID = getEntityId( response, 0 );
+
+        assertEquals( thing2Id, returnedUUID );
+
+        //get on the collection works, now get it directly by uuid
+
+        //now we should get thing1 from the loopback url
+
+        response = things.entity( thing1Id ).connection( "likes" ).entity( thing2Id ).get();
+
+        UUID returned = getEntityId( response, 0 );
+
+        assertEquals( "Should point to thing2 as an entity connection", thing2Id, returned );
+    }
+
+    @Test //USERGRID-3011
+    public void connectionsDeleteSecondEntityInConnectionTest() throws IOException {
+
+        CustomCollection things = context.collection( "things" );
+
+        UUID thing1Id = getEntityId( things.create( hashMap( "name", "thing1" ) ), 0 );
+
+        UUID thing2Id = getEntityId( things.create( hashMap( "name", "thing2" ) ), 0 );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //create the connection
+        things.entity( thing1Id ).connection( "likes" ).entity( thing2Id ).post();
+
+        JsonNode response = things.entity( "thing2" ).delete();
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        JsonNode node = things.entity ( "thing2" ).get();
+
+        assertNull(node);
+
+    }
+
+    @Test //USERGRID-3011
+    public void connectionsDeleteFirstEntityInConnectionTest() throws IOException {
+
+        CustomCollection things = context.collection( "things" );
+
+        UUID thing1Id = getEntityId( things.create( hashMap( "name", "thing1" ) ), 0 );
+
+        UUID thing2Id = getEntityId( things.create( hashMap( "name", "thing2" ) ), 0 );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        //create the connection
+        things.entity( thing1Id ).connection( "likes" ).entity( thing2Id ).post();
+
+        JsonNode response = things.entity( "thing1" ).delete();
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        JsonNode node = things.entity ( "thing1" ).get();
+
+        assertNull(node);
+
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/OwnershipResourceIT.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/OwnershipResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/OwnershipResourceIT.java
new file mode 100644
index 0000000..a514781
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/OwnershipResourceIT.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.users;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.apache.usergrid.cassandra.Concurrent;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.rest.TestContextSetup;
+import org.apache.usergrid.rest.test.resource.Connection;
+import org.apache.usergrid.rest.test.resource.CustomCollection;
+import org.apache.usergrid.rest.test.resource.app.queue.DevicesCollection;
+import org.apache.usergrid.rest.test.security.TestAppUser;
+import org.apache.usergrid.rest.test.security.TestUser;
+import org.apache.usergrid.utils.MapUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ *
+ */
+@Concurrent()
+public class OwnershipResourceIT extends AbstractRestIT {
+
+    @Rule
+    public TestContextSetup context = new TestContextSetup( this );
+
+
+    @Test
+    public void meVerify() throws Exception {
+
+        context.clearUser();
+
+        String email = "testuser1@usergrid.org";
+        TestUser user1 = new TestAppUser( email, "password", email ).create( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+        user1.login( context ).makeActive( context );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String token = user1.getToken();
+        JsonNode userNode = context.application().users().user( "me" ).get();
+        assertNotNull( userNode );
+
+        String uuid = userNode.get( "entities" ).get( 0 ).get( "uuid" ).textValue();
+        assertNotNull( uuid );
+
+        setup.getMgmtSvc().revokeAccessTokenForAppUser( token );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        try {
+            context.application().users().user( "me" ).get();
+            fail();
+        }
+        catch ( Exception ex ) {
+            ex.printStackTrace();
+            assertTrue( ex.getMessage().contains( "401" ) );
+        }
+    }
+
+
+    @Test
+    public void contextualPathOwnership() throws IOException {
+
+        // anonymous user
+        context.clearUser();
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        TestUser user1 = new TestAppUser( "testuser1@usergrid.org", "password", "testuser1@usergrid.org" ).create( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+        user1.login( context );
+        user1.makeActive( context );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // create device 1 on user1 devices
+        context.application().users().user( "me" ).devices()
+               .create( MapUtils.hashMap( "name", "device1" ).map( "number", "5551112222" ) );
+
+        // anonymous user
+        context.clearUser();
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // create device 2 on user 2
+        TestUser user2 = new TestAppUser( "testuser2@usergrid.org", "password", "testuser2@usergrid.org" ).create( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+        user2.login( context );
+        user2.makeActive( context );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        context.application().users().user( "me" ).devices()
+               .create( MapUtils.hashMap( "name", "device2" ).map( "number", "5552223333" ) );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // now query on user 1.
+
+        DevicesCollection devices = context.withUser( user1 ).application().users().user( "me" ).devices();
+
+        JsonNode data = devices.device( "device1" ).get();
+        assertNotNull( data );
+        assertEquals( "device1", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can't see device2
+        data = devices.device( "device2" ).get();
+        assertNull( data );
+
+        // do a collection load, make sure we're not loading device 2
+        data = devices.get();
+
+        assertEquals( "device1", getEntity( data, 0 ).get( "name" ).asText() );
+        assertNull( getEntity( data, 1 ) );
+
+        // log in as user 2 and check it
+        devices = context.withUser( user2 ).application().users().user( "me" ).devices();
+
+        data = devices.device( "device2" ).get();
+        assertNotNull( data );
+        assertEquals( "device2", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can't see device1
+        data = devices.device( "device1" ).get();
+        assertNull( data );
+
+        // do a collection load, make sure we're not loading device 1
+        data = devices.get();
+
+        assertEquals( "device2", getEntity( data, 0 ).get( "name" ).asText() );
+        assertNull( getEntity( data, 1 ) );
+
+        // we should see both devices when loaded from the root application
+
+        // test for user 1
+
+        devices = context.withUser( user1 ).application().devices();
+        data = devices.device( "device1" ).get();
+
+        assertNotNull( data );
+        assertEquals( "device1", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = devices.device( "device2" ).get();
+
+        assertNotNull( data );
+        assertEquals( "device2", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // test for user 2
+        data = context.withUser( user2 ).application().devices().device( "device1" ).get();
+
+        assertNotNull( data );
+        assertEquals( "device1", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = devices.device( "device2" ).get();
+
+        assertNotNull( data );
+        assertEquals( "device2", getEntity( data, 0 ).get( "name" ).asText() );
+    }
+
+
+    @Test
+    public void contextualConnectionOwnership() throws IOException {
+
+        // anonymous user
+        context.clearUser();
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String email = "testuser1@usergrid.org";
+        TestUser user1 = new TestAppUser( email, "password", email ).create( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+        user1.login( context ).makeActive( context );
+
+        // create a 4peaks restaurant
+        JsonNode data = context.application()
+                .collection( "restaurants" ).create( MapUtils.hashMap( "name", "4peaks" ) );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // create our connection
+        data = context.application().users().user( "me" )
+                .connection( "likes" ).collection( "restaurants" ).entity( "4peaks" ).post();
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+       String peaksId = getEntity( data, 0 ).get( "uuid" ).asText();
+
+        // anonymous user
+        context.clearUser();
+
+        // create a restaurant and link it to user 2
+        email = "testuser2@usergrid.org";
+        TestUser user2 = new TestAppUser( email, "password", email ).create( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        user2.login( context ).makeActive( context );
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        data = context.application().collection( "restaurants" )
+                      .create( MapUtils.hashMap( "name", "arrogantbutcher" ) );
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        data = context.application().users().user( "me" ).connection( "likes" ).collection( "restaurants" )
+                      .entity( "arrogantbutcher" ).post();
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String arrogantButcherId = getEntity( data, 0 ).get( "uuid" ).asText();
+
+        // now query on user 1.
+
+        CustomCollection likeRestaurants =
+                context.withUser( user1 ).application().users().user( "me" ).connection( "likes" )
+                       .collection( "restaurants" );
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // check we can get it via id
+        data = likeRestaurants.entity( peaksId ).get();
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can get it by name
+        data = likeRestaurants.entity( "4peaks" ).get();
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can't see arrogantbutcher by name or id
+        data = likeRestaurants.entity( "arrogantbutcher" ).get();
+        assertNull( data );
+
+        data = likeRestaurants.entity( arrogantButcherId ).get();
+        assertNull( data );
+
+        // do a collection load, make sure we're not entities we shouldn't see
+        data = likeRestaurants.get();
+
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+        assertNull( getEntity( data, 1 ) );
+
+        // log in as user 2 and check it
+        likeRestaurants = context.withUser( user2 ).application().users().user( "me" ).connection( "likes" )
+                                 .collection( "restaurants" );
+
+        data = likeRestaurants.entity( arrogantButcherId ).get();
+        assertNotNull( data );
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = likeRestaurants.entity( "arrogantbutcher" ).get();
+        assertNotNull( data );
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can't see 4peaks
+        data = likeRestaurants.entity( "4peaks" ).get();
+        assertNull( data );
+
+        data = likeRestaurants.entity( peaksId ).get();
+        assertNull( data );
+
+        // do a collection load, make sure we're not loading device 1
+        data = likeRestaurants.get();
+
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+        assertNull( getEntity( data, 1 ) );
+
+        // we should see both devices when loaded from the root application
+
+        // test for user 1
+
+        CustomCollection restaurants = context.withUser( user1 ).application().collection( "restaurants" );
+        data = restaurants.entity( "4peaks" ).get();
+
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = restaurants.entity( "arrogantbutcher" ).get();
+
+        assertNotNull( data );
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // test for user 2
+        restaurants = context.withUser( user1 ).application().collection( "restaurants" );
+        data = restaurants.entity( "4peaks" ).get();
+
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = restaurants.entity( "arrogantbutcher" ).get();
+
+        assertNotNull( data );
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+    }
+
+
+    @Test
+    public void contextualConnectionOwnershipGuestAccess() throws IOException {
+
+        //set up full GET,PUT,POST,DELETE access for guests
+        context.application().collection( "roles" ).entity( "guest" ).collection( "permissions" )
+               .create( MapUtils.hashMap( "permission", "get,put,post,delete:/**" ) );
+
+
+        // anonymous user
+        context.clearUser();
+
+
+        JsonNode city = context.application().collection( "cities" ).create( MapUtils.hashMap( "name", "tempe" ) );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        String cityId = getEntity( city, 0 ).get( "uuid" ).asText();
+
+        // create a 4peaks restaurant
+        JsonNode data = context.application().collection( "cities" ).entity( "tempe" ).connection( "likes" )
+                               .collection( "restaurants" ).create( MapUtils.hashMap( "name", "4peaks" ) );
+
+        String peaksId = getEntity( data, 0 ).get( "uuid" ).asText();
+
+        data = context.application().collection( "cities" ).entity( "tempe" ).connection( "likes" )
+                      .collection( "restaurants" ).create( MapUtils.hashMap( "name", "arrogantbutcher" ) );
+
+        String arrogantButcherId = getEntity( data, 0 ).get( "uuid" ).asText();
+
+        // now query on user 1.
+
+        Connection likeRestaurants =
+                context.application().collection( "cities" ).entity( "tempe" ).connection( "likes" );
+
+        refreshIndex(context.getOrgName(), context.getAppName());
+
+        // check we can get it via id with no collection name
+        data = likeRestaurants.entity( peaksId ).get();
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = likeRestaurants.entity( arrogantButcherId ).get();
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // check we can get it via id with a collection name
+        data = likeRestaurants.collection( "restaurants" ).entity( peaksId ).get();
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = likeRestaurants.collection( "restaurants" ).entity( arrogantButcherId ).get();
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+
+        // do a delete either token should work
+        data = likeRestaurants.collection( "restaurants" ).entity( peaksId ).delete();
+
+        assertNotNull( data );
+        assertEquals( "4peaks", getEntity( data, 0 ).get( "name" ).asText() );
+
+        data = likeRestaurants.collection( "restaurants" ).entity( arrogantButcherId ).delete();
+
+        assertNotNull( data );
+        assertEquals( "arrogantbutcher", getEntity( data, 0 ).get( "name" ).asText() );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/3993f081/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/PermissionsResourceIT.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/PermissionsResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/PermissionsResourceIT.java
new file mode 100644
index 0000000..a07622e
--- /dev/null
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/applications/collection/users/PermissionsResourceIT.java
@@ -0,0 +1,768 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.rest.applications.collection.users;
+
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.ws.rs.core.MediaType;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.apache.usergrid.cassandra.Concurrent;
+import org.apache.usergrid.java.client.entities.Group;
+import org.apache.usergrid.management.ApplicationInfo;
+import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.apache.usergrid.rest.AbstractRestIT;
+import org.apache.usergrid.utils.UUIDUtils;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.apache.usergrid.utils.MapUtils.hashMap;
+
+
+/**
+ * Tests permissions of adding and removing users from roles as well as groups
+ *
+ * @author tnine
+ */
+@Concurrent()
+public class PermissionsResourceIT extends AbstractRestIT {
+
+    private static final String ROLE = "permtestrole";
+
+    private static final String USER = "edanuff";
+
+
+    public PermissionsResourceIT() throws Exception {
+
+    }
+
+
+    @Test
+    public void deleteUserFromRole() throws IOException {
+        Map<String, String> data = hashMap( "name", ROLE );
+
+        JsonNode node = mapper.readTree( resource().path( "/test-organization/test-app/roles" ).queryParam( "access_token", access_token )
+                        .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                        .post( String.class, data ));
+
+        assertNull( node.get( "error" ) );
+
+        assertEquals( ROLE, getEntity( node, 0 ).get( "name" ).asText() );
+
+        refreshIndex("test-organization", "test-app");
+
+        // add the user to the role
+        node = mapper.readTree( resource().path( "/test-organization/test-app/roles/" + ROLE + "/users/" + USER )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        assertNull( node.get( "error" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        // now check the user has the role
+        node = mapper.readTree( resource().path( "/test-organization/test-app/users/" + USER + "/roles" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+
+        // check if the role was assigned
+        assertEquals( ROLE, getEntity( node, 0 ).get( "name" ).asText() );
+
+        // now delete the role
+        node = mapper.readTree( resource().path( "/test-organization/test-app/users/" + USER + "/roles/" + ROLE )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+
+        refreshIndex("test-organization", "test-app");
+
+        // check if the role was deleted
+
+        node = mapper.readTree( resource().path( "/test-organization/test-app/users/" + USER + "/roles" )
+                .queryParam( "access_token", access_token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+
+        // check if the role was assigned
+        assertNull( getEntity( node, 0 ) );
+    }
+
+
+    @Test
+    public void deleteUserGroup() throws IOException {
+
+        // don't populate the user, it will use the currently authenticated
+        // user.
+
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String groupPath = "groupPath" + id;
+
+        Map<String, String> data = hashMap( "type", "group" ).map( "path", groupPath );
+
+        JsonNode node = mapper.readTree( resource().path( "/test-organization/test-app/groups" )
+                .queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE )
+                .post( String.class, data ));
+
+        assertNull( node.get( "error" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        node = mapper.readTree( 
+            resource().path( "/test-organization/test-app/groups/" + groupPath + "/users/" + USER )
+                .queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE )
+                .post( String.class ));
+
+        assertNull( node.get( "error" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        Map<String, Group> groups = client.getGroupsForUser( USER );
+
+        assertNotNull( groups.get( groupPath ) );
+
+        // now delete the group
+
+        node = mapper.readTree( 
+            resource().path( "/test-organization/test-app/groups/" + groupPath + "/users/" + USER )
+                .queryParam( "access_token", access_token )
+                .accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE )
+                .delete( String.class ));
+
+        assertNull( node.get( "error" ) );
+
+        refreshIndex("test-organization", "test-app");
+
+        groups = client.getGroupsForUser( USER );
+
+        assertNull( groups.get( groupPath ) );
+    }
+
+
+    /**
+     * For the record, you should NEVER allow the guest role to add roles. This is a gaping security hole and a VERY BAD
+     * IDEA! That being said, this should technically work, and needs testing.
+     */
+    @Test
+    public void dictionaryPermissions() throws Exception {
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String applicationName = "testapp";
+        String orgname = "dictionaryPermissions";
+        String username = "permissionadmin" + id;
+        String password = "password";
+        String email = String.format( "email%s@usergrid.com", id );
+
+        OrganizationOwnerInfo orgs = setup.getMgmtSvc()
+                                          .createOwnerAndOrganization( orgname, username, "noname", email, password,
+                                                  true, false );
+
+        // create the app
+        ApplicationInfo appInfo =
+                setup.getMgmtSvc().createApplication( orgs.getOrganization().getUuid(), applicationName );
+
+        String adminToken = setup.getMgmtSvc().getAccessTokenForAdminUser( orgs.getOwner().getUuid(), 0 );
+
+        // add the perms to the guest to allow users in the role to create roles
+        // themselves
+        addPermission( orgname, applicationName, adminToken, "guest", "get,put,post:/roles/**" );
+
+        Map<String, String> data = hashMap( "name", "usercreatedrole" );
+
+        // create a role as the user
+        JsonNode node = mapper.readTree( resource().path( String.format( "/%s/%s/roles", orgname, applicationName ) )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                .post( String.class, data ));
+
+        assertNull( getError( node ) );
+
+        refreshIndex(orgname, applicationName);
+
+        // now try to add permission as the user, this should work
+        addPermission( orgname, applicationName, "usercreatedrole", "get,put,post:/foo/**" );
+    }
+
+
+    /**
+     * Tests a real world example with the following steps. Creates an application.
+     * <p/>
+     * Creates a new role "reviewer"
+     * <p/>
+     * Grants a permission to GET, POST, and PUT the reviews url for the reviewer role
+     * <p/>
+     * Grants a permission GET on the reviewer for the
+     * <p/>
+     * Create a user reviewer1 and add them to the reviewer role
+     * <p/>
+     * Test access with reviewer1
+     * <p/>
+     * Create a group reviewergroup and add the "reviewer" group to it
+     * <p/>
+     * Create a user reviewer 2 and add them to the "reveiwergroup"
+     */
+    @Test
+    public void applicationPermissions() throws Exception {
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String applicationName = "test";
+        String orgname = "applicationpermissions";
+        String username = "permissionadmin" + id;
+        String password = "password";
+        String email = String.format( "email%s@usergrid.com", id );
+
+        OrganizationOwnerInfo orgs = setup.getMgmtSvc()
+                                          .createOwnerAndOrganization( orgname, username, "noname", email, password,
+                                                  true, false );
+
+        // create the app
+        ApplicationInfo appInfo =
+                setup.getMgmtSvc().createApplication( orgs.getOrganization().getUuid(), applicationName );
+
+        // now create the new role
+        Map<String, String> data = hashMap( "name", "reviewer" );
+
+        String adminToken = setup.getMgmtSvc().getAccessTokenForAdminUser( orgs.getOwner().getUuid(), 0 );
+
+        JsonNode node = mapper.readTree( resource().path( String.format( "/%s/%s/roles", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, data ));
+
+        assertNull( getError( node ) );
+
+        // delete the default role to test permissions later
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/roles/default", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+
+        assertNull( getError( node ) );
+        refreshIndex(orgname, applicationName);
+
+        // grant the perms to reviewer
+        addPermission( orgname, applicationName, adminToken, "reviewer", "get,put,post:/reviews/**" );
+
+        // grant get to guest
+        addPermission( orgname, applicationName, adminToken, "guest", "get:/reviews/**" );
+
+        UUID userId = createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "reviewer1",
+                "reviewer1@usergrid.com" );
+
+        refreshIndex(orgname, applicationName);
+
+        // grant this user the "reviewer" role
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/users/reviewer1/roles/reviewer", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        assertNull( getError( node ) );
+
+        refreshIndex(orgname, applicationName);
+
+        String reviewer1Token = setup.getMgmtSvc().getAccessTokenForAppUser( appInfo.getId(), userId, 0 );
+
+        Map<String, String> review =
+                hashMap( "rating", "4" ).map( "name", "noca" ).map( "review", "Excellent service and food" );
+
+        // post a review as the reviewer1 user
+        resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", reviewer1Token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, review );
+
+        review = hashMap( "rating", "4" ).map( "name", "4peaks" ).map( "review", "Huge beer selection" );
+
+        refreshIndex(orgname, applicationName);
+
+        // put a review as the reviewer1 user
+        resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", reviewer1Token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).put( String.class, review );
+
+        refreshIndex(orgname, applicationName);
+
+        // get the reviews
+
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", reviewer1Token ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+
+        assertEquals( "noca", getEntity( node, 0 ).get( "name" ).asText() );
+        assertEquals( "4peaks", getEntity( node, 1 ).get( "name" ).asText() );
+
+        // can't delete, not in the grants
+
+        ClientResponse.Status status = null;
+
+        try {
+            resource().path( String.format( "/%s/%s/reviews/noca", orgname, applicationName ) )
+                    .queryParam( "access_token", reviewer1Token ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class );
+        }
+        catch ( UniformInterfaceException uie ) {
+            status = uie.getResponse().getClientResponseStatus();
+        }
+
+        assertEquals( Status.UNAUTHORIZED, status );
+
+        refreshIndex(orgname, applicationName);
+
+        status = null;
+
+        try {
+            resource().path( String.format( "/%s/%s/reviews/4peaks", orgname, applicationName ) )
+                    .queryParam( "access_token", reviewer1Token ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class );
+        }
+        catch ( UniformInterfaceException uie ) {
+            status = uie.getResponse().getClientResponseStatus();
+        }
+
+        assertEquals( Status.UNAUTHORIZED, status );
+
+        refreshIndex(orgname, applicationName);
+
+        // now test some groups
+        UUID secondUserId = createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "reviewer2",
+                "reviewer2@usergrid.com" );
+
+        Map<String, String> group = hashMap( "path", "reviewergroup" );
+
+        // /now create the group
+        resource().path( String.format( "/%s/%s/groups", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, group );
+
+        refreshIndex(orgname, applicationName);
+
+        // link the group to the role
+        resource().path( String.format( "/%s/%s/groups/reviewergroup/roles/reviewer", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, group );
+
+        refreshIndex(orgname, applicationName);
+
+        // add the user to the group
+        resource().path( String.format( "/%s/%s/users/reviewer2/groups/reviewergroup", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class );
+
+        refreshIndex(orgname, applicationName);
+
+        // post 2 reviews. Should get permissions from the group
+
+        String secondUserToken = setup.getMgmtSvc().getAccessTokenForAppUser( appInfo.getId(), secondUserId, 0 );
+
+        review = hashMap( "rating", "4" ).map( "name", "cowboyciao" ).map( "review", "Great atmosphoere" );
+
+        // post a review as the reviewer2 user
+        resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", secondUserToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, review );
+
+        review = hashMap( "rating", "4" ).map( "name", "currycorner" ).map( "review", "Authentic" );
+
+        refreshIndex(orgname, applicationName);
+
+        // post a review as the reviewer2 user
+        resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", secondUserToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, review );
+
+        refreshIndex(orgname, applicationName);
+
+        // get all reviews as a user
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/reviews", orgname, applicationName ) )
+                .queryParam( "access_token", secondUserToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+
+        assertEquals( "noca", getEntity( node, 0 ).get( "name" ).asText() );
+        assertEquals( "4peaks", getEntity( node, 1 ).get( "name" ).asText() );
+        assertEquals( "cowboyciao", getEntity( node, 2 ).get( "name" ).asText() );
+        assertEquals( "currycorner", getEntity( node, 3 ).get( "name" ).asText() );
+
+        // issue a delete, it shouldn't work, no permissions
+
+        status = null;
+
+        try {
+            resource().path( String.format( "/%s/%s/reviews/cowboyciao", orgname, applicationName ) )
+                    .queryParam( "access_token", secondUserToken ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class );
+        }
+        catch ( UniformInterfaceException uie ) {
+            status = uie.getResponse().getClientResponseStatus();
+        }
+
+        assertEquals( Status.UNAUTHORIZED, status );
+
+        refreshIndex(orgname, applicationName);
+
+        status = null;
+
+        try {
+            resource().path( String.format( "/%s/%s/reviews/currycorner", orgname, applicationName ) )
+                    .queryParam( "access_token", secondUserToken ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class );
+        }
+        catch ( UniformInterfaceException uie ) {
+            status = uie.getResponse().getClientResponseStatus();
+        }
+
+        assertEquals( Status.UNAUTHORIZED, status );
+    }
+
+
+    /**
+     * Tests the scenario where we have roles declarations such as: <ul> <li>GET /users/[star]/reviews "any user can
+     * read any others book review"</li> <li>POST /users/[user1]/reviews "cannot post as user2 to user1's reviews"</li>
+     * <ii>POST /users/[star]/reviews/feedback/* "can post as user2 to user1's feedback/good or /bad</ii> </ul>
+     * <p/>
+     * Scenario is as follows: Create an application
+     * <p/>
+     * Add two application users - user1 - user2
+     * <p/>
+     * Create a book collection for user1
+     */
+    @Test
+    public void wildcardMiddlePermission() throws Exception {
+
+         Map<String, String> params = buildOrgAppParams();
+        String orgname =params.get( "orgName" ) ;
+        String applicationName = params.get( "appName" ) ;
+        
+        OrganizationOwnerInfo orgs = setup.getMgmtSvc().createOwnerAndOrganization( params.get( "orgName" ),
+                params.get( "username" ), "noname", params.get( "email" ), params.get( "password" ), true, false );
+
+        // create the app
+        ApplicationInfo appInfo =
+                setup.getMgmtSvc().createApplication( orgs.getOrganization().getUuid(), params.get( "appName" ) );
+        assertNotNull( appInfo );
+
+        String adminToken = setup.getMgmtSvc().getAccessTokenForAdminUser( orgs.getOwner().getUuid(), 0 );
+
+        JsonNode node = mapper.readTree( resource()
+                .path( String.format( "/%s/%s/roles/default", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+        Map<String, String> data = hashMap( "name", "reviewer" );
+
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/roles", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, data ));
+        assertNull( getError( node ) );
+
+        refreshIndex(orgname, applicationName);
+
+        // allow access to reviews
+        addPermission( params.get( "orgName" ), params.get( "appName" ), adminToken, "reviewer",
+                "get,put,post:/reviews/**" );
+        // allow access to all user's connections
+        addPermission( params.get( "orgName" ), params.get( "appName" ), adminToken, "reviewer",
+                "get,put,post:/users/${user}/**" );
+        // allow access to the review relationship
+        addPermission( params.get( "orgName" ), params.get( "appName" ), adminToken, "reviewer",
+                "get,put,post:/books/*/review/*" );
+
+        assertNull( getError( node ) );
+        // create userOne
+        UUID userOneId =
+                createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "wildcardpermuserone",
+                        "wildcardpermuserone@apigee.com" );
+        assertNotNull( userOneId );
+
+        // create userTwo
+        UUID userTwoId =
+                createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "wildcardpermusertwo",
+                        "wildcardpermusertwo@apigee.com" );
+        assertNotNull( userTwoId );
+
+        refreshIndex(orgname, applicationName);
+
+        // assign userOne the reviewer role
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/%s/roles/reviewer", params.get( "orgName" ), params.get( "appName" ),
+                        userOneId.toString() ) ).queryParam( "access_token", adminToken )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        refreshIndex(orgname, applicationName);
+
+        Map<String, String> book = hashMap( "title", "Ready Player One" ).map( "author", "Earnest Cline" );
+
+        // create a book as admin
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/books", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, book ));
+
+        logNode( node );
+        assertEquals( "Ready Player One", getEntity( node, 0 ).get( "title" ).textValue() );
+        String bookId = getEntity( node, 0 ).get( "uuid" ).textValue();
+
+        refreshIndex(orgname, applicationName);
+
+        String userOneToken = setup.getMgmtSvc().getAccessTokenForAppUser( appInfo.getId(), userOneId, 0 );
+        // post a review of the book as user1
+        // POST https://api.usergrid.com/my-org/my-app/users/$user1/reviewed/books/$uuid
+        Map<String, String> review =
+                hashMap( "heading", "Loved It" ).map( "body", "80s Awesomeness set in the future" );
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/reviews", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, review ));
+        String reviewId = getEntity( node, 0 ).get( "uuid" ).textValue();
+
+        refreshIndex(orgname, applicationName);
+
+        // POST https://api.usergrid.com/my-org/my-app/users/me/wrote/review/${reviewId}
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/me/wrote/review/%s", params.get( "orgName" ), params.get( "appName" ),
+                        reviewId ) ).queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        refreshIndex(orgname, applicationName);
+
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/me/reviewed/books/%s", params.get( "orgName" ), params.get( "appName" ),
+                        bookId ) ).queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+        logNode( node );
+
+        refreshIndex(orgname, applicationName);
+
+        // POST https://api.usergrid.com/my-org/my-app/books/${bookId}/review/${reviewId}
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/books/%s/review/%s", params.get( "orgName" ), params.get( "appName" ), bookId,
+                        reviewId ) ).queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+        logNode( node );
+
+        refreshIndex(orgname, applicationName);
+
+        // now try to post the same thing to books to verify as userOne the failure
+        Status status = null;
+        try {
+            node = mapper.readTree( resource().path( String.format( "/%s/%s/books", params.get( "orgName" ), params.get( "appName" ) ) )
+                    .queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                    .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+            logNode( node );
+        }
+        catch ( UniformInterfaceException uie ) {
+            status = uie.getResponse().getClientResponseStatus();
+        }
+        assertEquals( Status.UNAUTHORIZED, status );
+
+        refreshIndex(orgname, applicationName);
+
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/me/reviewed/books", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        logNode( node );
+
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/reviews/%s", params.get( "orgName" ), params.get( "appName" ), reviewId ) )
+                .queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        logNode( node );
+
+        node = mapper.readTree( resource()
+                .path( String.format( "/%s/%s/users/me/wrote", params.get( "orgName" ), params.get( "appName" ) ) )
+                .queryParam( "access_token", userOneToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+        logNode( node );
+    }
+
+
+    /**
+     * Tests the scenario where we have role declaration such as: <ul> <li>POST /users/[star]/following/users/${user}" a
+     * user can add himself to any other user following list"</li> </ul>
+     * <p/>
+     * Scenario is as follows: Create an application
+     * <p/>
+     * Add two application users - examplepatient - exampledoctor
+     * <p/>
+     * examplepatient add himself to exampledoctor following list
+     */
+    @Test
+    @Ignore("Why is this ignored?")
+    public void wildcardFollowingPermission() throws Exception {
+        UUID id = UUIDUtils.newTimeUUID();
+
+        String applicationName = "test";
+        String orgname = "followingpermissions";
+        String username = "permissionadmin" + id;
+        String password = "password";
+        String email = String.format( "email%s@usergrid.com", id );
+
+        OrganizationOwnerInfo orgs = setup.getMgmtSvc()
+                                          .createOwnerAndOrganization( orgname, username, "noname", email, password,
+                                                  true, false );
+
+        // create the app
+        ApplicationInfo appInfo =
+                setup.getMgmtSvc().createApplication( orgs.getOrganization().getUuid(), applicationName );
+        assertNotNull( appInfo );
+
+        String adminToken = setup.getMgmtSvc().getAccessTokenForAdminUser( orgs.getOwner().getUuid(), 0 );
+
+        JsonNode node = mapper.readTree( resource().path( String.format( "/%s/%s/roles/default", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).delete( String.class ));
+        Map<String, String> data = hashMap( "name", "patient" );
+
+        node = mapper.readTree( resource().path( String.format( "/%s/%s/roles", orgname, applicationName ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class, data ));
+        assertNull( getError( node ) );
+        //allow patients to add doctors as their followers
+        addPermission( orgname, applicationName, adminToken, "patient",
+                "delete,post:/users/*/following/users/${user}" );
+
+        assertNull( getError( node ) );
+        // create examplepatient
+        UUID patientId =
+                createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "examplepatient",
+                        "examplepatient@apigee.com" );
+        assertNotNull( patientId );
+
+        // create exampledoctor
+        UUID doctorId = createRoleUser( orgs.getOrganization().getUuid(), appInfo.getId(), adminToken, "exampledoctor",
+                "exampledoctor@apigee.com" );
+        assertNotNull( doctorId );
+
+
+        // assign examplepatient the patient role
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/%s/roles/patient", orgname, applicationName, patientId.toString() ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+
+        String patientToken = setup.getMgmtSvc().getAccessTokenForAppUser( appInfo.getId(), patientId, 0 );
+
+        node = mapper.readTree( resource().path( String
+                .format( "/%s/%s/users/%s/following/users/%s", orgname, applicationName, "exampledoctor",
+                        "examplepatient" ) ).queryParam( "access_token", patientToken )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).post( String.class ));
+        logNode( node );
+    }
+
+
+    private Map<String, String> buildOrgAppParams() {
+        UUID id = UUIDUtils.newTimeUUID();
+        Map<String, String> props =
+                hashMap( "username", "wcpermadmin" ).map( "orgName", "orgnamewcperm" ).map( "appName", "test" )
+                        .map( "password", "password" )
+                        .map( "email", String.format( "email%s@apigee.com", id.toString() ) );
+
+        return props;
+    }
+
+
+    /**
+     * Create the user, check there are no errors
+     *
+     * @return the userid
+     */
+    private UUID createRoleUser( UUID orgId, UUID appId, String adminToken, String username, String email )
+            throws Exception {
+
+        Map<String, String> props = hashMap( "email", email ).map( "username", username ).map( "name", username )
+                .map( "password", "password" );
+
+        JsonNode node = mapper.readTree( resource().path( String.format( "/%s/%s/users", orgId, appId ) )
+                .queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).put( String.class, props ));
+
+        assertNull( getError( node ) );
+
+        UUID userId = UUID.fromString( getEntity( node, 0 ).get( "uuid" ).asText() );
+
+        // manually activate user
+        setup.getMgmtSvc().activateAppUser( appId, userId );
+
+        return userId;
+    }
+
+
+    /** Test adding the permission to the role */
+    private void addPermission( String orgname, String appname, String adminToken, String rolename, String grant ) throws IOException {
+        Map<String, String> props = hashMap( "permission", grant );
+
+        String rolePath = String.format( "/%s/%s/roles/%s/permissions", orgname, appname, rolename );
+
+        JsonNode node = mapper.readTree( resource().path( rolePath ).queryParam( "access_token", adminToken )
+                .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                .put( String.class, props ));
+
+        assertNull( getError( node ) );
+
+        node = mapper.readTree( resource().path( rolePath ).queryParam( "access_token", adminToken ).accept( MediaType.APPLICATION_JSON )
+                .type( MediaType.APPLICATION_JSON_TYPE ).get( String.class ));
+
+        ArrayNode data = ( ArrayNode ) node.get( "data" );
+
+        Iterator<JsonNode> iterator = data.elements();
+
+        while ( iterator.hasNext() ) {
+            if ( grant.equals( iterator.next().asText() ) ) {
+                return;
+            }
+        }
+
+        fail( String.format( "didn't find grant %s in the results", grant ) );
+    }
+
+
+    /** Test adding the permission to the role */
+    private void addPermission( String orgname, String appname, String rolename, String grant ) throws IOException {
+        Map<String, String> props = hashMap( "permission", grant );
+
+        String rolePath = String.format( "/%s/%s/roles/%s/permissions", orgname, appname, rolename );
+
+        JsonNode node = mapper.readTree( resource().path( rolePath ).accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                        .put( String.class, props ));
+
+        assertNull( getError( node ) );
+
+        node = mapper.readTree( resource().path( rolePath ).accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE )
+                .get( String.class ));
+
+        ArrayNode data = ( ArrayNode ) node.get( "data" );
+
+        Iterator<JsonNode> iterator = data.elements();
+
+        while ( iterator.hasNext() ) {
+            if ( grant.equals( iterator.next().asText() ) ) {
+                return;
+            }
+        }
+
+        fail( String.format( "didn't find grant %s in the results", grant ) );
+    }
+}