/*
* 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 com.fasterxml.jackson.databind.JsonNode;
import org.apache.usergrid.rest.test.resource.AbstractRestIT;
import org.apache.usergrid.rest.test.resource.model.Collection;
import org.apache.usergrid.rest.test.resource.model.Entity;
import org.junit.Test;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.NotAuthorizedException;
import java.io.IOException;
import static org.junit.Assert.*;
/**
* @author rockerston
*
* REST tests for /groups endpoint
*
* */
public class GroupResourceIT extends AbstractRestIT {
public GroupResourceIT() throws Exception { }
/***
*
* helper method to create a group
*
*/
private Entity createGroup(String groupName, String groupPath) throws IOException{
String title = "title";
return createGroup(groupName, groupPath, title);
}
private Entity createGroup(String groupName, String groupPath, String groupTitle) throws IOException{
Entity payload = new Entity();
payload.put("name", groupName);
payload.put("path", groupPath);
payload.put("title", groupTitle);
Entity entity = this.app().collection("groups").post(payload);
assertEquals(entity.get("name"), groupName);
assertEquals(entity.get("path"), groupPath);
this.waitForQueueDrainAndRefreshIndex();
return entity;
}
/***
*
* helper method to create a role
*
*/
private Entity createRole(String roleName, String roleTitle) throws IOException{
Entity payload = new Entity();
payload.put("name", roleName);
payload.put("title", roleTitle);
Entity entity = this.app().collection("roles").post(payload);
assertEquals(entity.get("name"), roleName);
assertEquals(entity.get("title"), roleTitle);
this.waitForQueueDrainAndRefreshIndex();
return entity;
}
/***
*
* helper method to create an app level user
*
*/
private Entity createUser(String username, String email, String password) throws IOException{
Entity payload = new Entity();
payload.put("username", username);
payload.put("email", email);
payload.put("password", password);
Entity entity = this.app().collection("users").post(payload);
assertEquals(entity.get("username"), username);
assertEquals(entity.get("email"), email);
this.waitForQueueDrainAndRefreshIndex();
return entity;
}
/***
*
* Verify that we can create a group with a standard string in the name and path
*
*/
@Test()
public void createGroupValidation() throws IOException {
String groupName = "testgroup";
String groupPath = "testgroup";
this.createGroup( groupName, groupPath );
}
/***
*
* Verify that we can create a group with a slash in the name and path
*
*/
@Test()
public void createGroupSlashInNameAndPathValidation() throws IOException {
String groupNameSlash = "test/group";
String groupPathSlash = "test/group";
this.createGroup(groupNameSlash, groupPathSlash);
}
/***
*
* Verify that we can create a group with a space in the name
*
*/
@Test()
public void createGroupSpaceInNameValidation() throws IOException {
String groupSpaceName = "test group";
String groupPath = "testgroup";
this.createGroup(groupSpaceName, groupPath);
}
/***
*
* Verify that we cannot create a group with a space in the path
*
*/
@Test()
public void createGroupSpaceInPathValidation() throws IOException {
String groupName = "testgroup";
String groupSpacePath = "test group";
try {
this.createGroup(groupName, groupSpacePath);
fail("Should not be able to create a group with a space in the path");
} catch (ClientErrorException e) {
//verify the correct error was returned
JsonNode node = mapper.readTree( e.getResponse().readEntity( String.class ));
assertEquals( "illegal_argument", node.get( "error" ).textValue() );
}
}
/***
*
* Verify that we can create a group, change the name, then delete it
*
*/
@Test()
public void groupCRUDOperations() throws IOException {
//1. create a group
String groupName = "testgroup";
String groupPath = "testgroup";
Entity group = this.createGroup(groupName, groupPath);
//2. do a GET to verify the property really was set
Entity groupResponseGET = this.app().collection("groups").entity(group).get();
assertEquals(groupResponseGET.get("path"), groupPath);
//3. change the name
String newGroupPath = "newtestgroup";
group.put("path", newGroupPath);
Entity groupResponse = this.app().collection("groups").entity(group).put(group);
assertEquals(groupResponse.get("path"), newGroupPath);
this.waitForQueueDrainAndRefreshIndex();
//4. do a GET to verify the property really was set
groupResponseGET = this.app().collection("groups").entity(group).get();
assertEquals(groupResponseGET.get("path"), newGroupPath);
//5. now delete the group
this.app().collection("groups").entity(group).delete();
//6. do a GET to make sure the entity was deleted
try {
this.app().collection("groups").uniqueID(groupName).get();
fail("Entity still exists");
} catch (ClientErrorException e) {
//verify the correct error was returned
JsonNode node = mapper.readTree( e.getResponse().readEntity( String.class ));
assertEquals( "entity_not_found", node.get( "error" ).textValue() );
}
}
/***
*
* Verify that we can create a group, user, add user to group, delete connection
*
*/
@Test()
public void addRemoveUserGroup() throws IOException {
//1. create a group
String groupName = "testgroup";
String groupPath = "testgroup";
Entity group = this.createGroup(groupName, groupPath);
// 2. create a user
String username = "fred";
String email = "fred@usergrid.com";
String password = "password";
Entity user = this.createUser(username, email, password);
// 3. add the user to the group
Entity response = this.app().collection("users").entity(user).connection().collection("groups").entity(group).post();
assertEquals(response.get("name"), groupName);
this.waitForQueueDrainAndRefreshIndex();
// 4. make sure the user is in the group
Collection collection = this.app().collection("groups").entity(group).connection().collection("users").get();
Entity entity = collection.next();
assertEquals(entity.get("username"), username);
//5. try it the other way around
collection = this.app().collection("users").entity(user).connection().collection("groups").get();
entity = collection.next();
assertEquals(entity.get("name"), groupName);
//6. remove the user from the group
this.app().collection("group").entity(group).connection().collection("users").entity(user).delete();
this.waitForQueueDrainAndRefreshIndex();
//6. make sure the connection no longer exists
collection = this.app().collection("group").entity(group).connection().collection("users").get();
assertEquals(collection.hasNext(), false);
//8. do a GET to make sure the user still exists and did not get deleted with the collection delete
Entity userEntity = this.app().collection("user").entity(user).get();
assertEquals(userEntity.get("username"), username);
}
/***
*
* Verify that we can create a group, role, add role to group, delete connection
*
*/
@Test
public void addRemoveRoleGroup() throws Exception {
//1. create a group
String groupName = "testgroup";
String groupPath = "testgroup";
Entity group = this.createGroup(groupName, groupPath);
//2. create a role
String roleName = "tester";
String roleTitle = "tester";
Entity role = this.createRole(roleName, roleTitle);
this.waitForQueueDrainAndRefreshIndex();
//3. add role to the group
Entity response = this.app().collection("roles").entity(role).connection().collection("groups").entity(group).post();
assertEquals(response.get("name"), groupName);
this.waitForQueueDrainAndRefreshIndex();
//4. make sure the role is in the group
Collection collection = this.app().collection("groups").entity(group).connection().collection("roles").get();
Entity entity = collection.next();
assertEquals(entity.get("name"), roleName);
//5. remove Role from the group (should only delete the connection)
this.app().collection("groups").entity(group).connection().collection("roles").entity(role).delete();
this.waitForQueueDrainAndRefreshIndex();
//6. make sure the connection no longer exists
collection = this.app().collection("groups").entity(group).connection().collection("roles").get();
if (collection.hasNext()) {
fail("Entity still exists");
}
//7. check root roles to make sure role still exists
role = this.app().collection("roles").uniqueID(roleName).get();
assertEquals(role.get("name"), roleName);
//8. delete the role
this.app().collection("role").entity(role).delete();
this.waitForQueueDrainAndRefreshIndex();
Thread.sleep(5000);
//9. do a GET to make sure the role was deleted
try {
Entity entity1 = this.app().collection("role").entity(role).get();
fail("Entity still exists");
} catch (ClientErrorException e) {
//verify the correct error was returned
JsonNode node = mapper.readTree( e.getResponse().readEntity( String.class ));
assertEquals( "entity_not_found", node.get( "error" ).textValue() );
}
}
/***
*
* Verify that group / role permissions work
*
*/
@Test()
public void addRolePermissionToGroupVerifyPermission() throws IOException {
//1. create a group
String groupName = "testgroup";
String groupPath = "testgroup";
Entity group = this.createGroup(groupName, groupPath);
//2. create a user
String username = "fred";
String email = "fred@usergrid.com";
String password = "password";
Entity user = this.createUser(username, email, password);
//3. create a role
String roleName = "tester";
String roleTitle = "tester";
Entity role = this.createRole(roleName, roleTitle);
//4. add permissions to role
Entity payload = new Entity();
payload.put("permission","get,post:/cats/*");
Entity permission = this.app().collection("roles").uniqueID(roleName).connection("permissions").post(payload);
assertEquals(permission.get("data"), "get,post:/cats/*");
//5. add role to the group
Entity addRoleResponse = this.app().collection("groups").entity(group).connection().collection("roles").entity(role).post();
assertEquals(addRoleResponse.get("name"), roleName);
//6. add user to group
Entity addUserResponse = this.app().collection("users").entity(user).connection().collection("groups").entity(group).post();
assertEquals(addUserResponse.get("name"), groupName);
//7. delete the default role
this.app().collection("role").uniqueID("Default").delete();
//8. log user in, should then be using the app user's token not the admin token
this.getAppUserToken(username, password);
//9. create a cat - permissions should allow this
String catName = "fluffy";
payload = new Entity();
payload.put("name", catName);
Entity fluffy = this.app().collection("cats").post(payload);
assertEquals(fluffy.get("name"), catName);
this.waitForQueueDrainAndRefreshIndex();
//10. get the cat - permissions should allow this
fluffy = this.app().collection("cats").uniqueID(catName).get();
assertEquals(fluffy.get("name"), catName);
//11. edit the cat - permissions should not allow this
fluffy.put("color", "brown");
try {
this.app().collection("cats").uniqueID(catName).put(fluffy);
fail("permissions should not allow this");
} catch (ClientErrorException e) {
//verify the correct error was returned
JsonNode node = mapper.readTree( e.getResponse().readEntity( String.class ) );
assertEquals( "unauthorized", node.get( "error" ).textValue() );
}
//12. delete the cat - permissions should not allow this
try {
this.app().collection("cats").uniqueID(catName).delete();
fail("permissions should not allow this");
} catch (ClientErrorException e) {
//verify the correct error was returned
JsonNode node = mapper.readTree( e.getResponse().readEntity( String.class ));
assertEquals( "unauthorized", node.get( "error" ).textValue() );
}
}
/**
* Post a group activities and test that the appear in the groups activities feed, and that
* they appear only in the personal activity feeds of the group's members.
*/
@Test
public void postPrivateGroupActivity() throws IOException {
//1. create a group
String groupName = "testgroup";
String groupPath = "testgroup";
Entity group = this.createGroup(groupName, groupPath);
//2. create user 1
String user1Username = "fred";
String user1Email = "fred@usergrid.com";
String password = "password";
Entity user1 = this.createUser(user1Username, user1Email, password);
//3. create user 2
String user2Username = "barney";
String user2Email = "barney@usergrid.com";
password = "password";
Entity user2 = this.createUser(user2Username, user2Email, password);
//4. create user 3
String user3Username = "wilma";
String user3Email = "wilma@usergrid.com";
password = "password";
Entity user3 = this.createUser(user3Username, user3Email, password);
//5. add user1 to the group
Entity addUser1Response = this.app().collection("users")
.entity(user1).connection().collection("groups").entity(group).post();
assertEquals(addUser1Response.get("name"), groupName);
//6. add user2 to the group
Entity addUser2Response = this.app().collection("users")
.entity(user2).connection().collection("groups").entity(group).post();
assertEquals(addUser2Response.get("name"), groupName);
// user 3 does not get added to the group
//7. get all the users in the groups
this.waitForQueueDrainAndRefreshIndex();
Collection usersInGroup = this.app().collection("groups").uniqueID(groupName).connection("users").get();
assertEquals(usersInGroup.getResponse().getEntityCount(), 2);
//7a. delete default role and make group private, only accessible to those with "group1role"
this.app().collection("role").uniqueID("Default").delete();
Entity data = new Entity().chainPut("name", "group1role");
this.app().collection("roles").post(data);
this.waitForQueueDrainAndRefreshIndex();
Entity perms = new Entity();
String permission = "get,post,put,delete:/groups/" + group.getUuid() + "/**";
perms.put("permission",permission);
this.app().collection("roles").uniqueID("group1role").connection("permissions").post(perms);
this.app().collection("roles").uniqueID("group1role").connection("users").uniqueID( user1Username ).post();
this.app().collection("roles").uniqueID("group1role").connection("users").uniqueID( user2Username ).post();
this.waitForQueueDrainAndRefreshIndex();
//7b. everybody gets access to /activities
perms = new Entity();
permission = "get:/activities/**";
perms.put("permission",permission);
this.app().collection("roles").uniqueID("Guest").connection("permissions").post(perms);
this.waitForQueueDrainAndRefreshIndex();
this.app().collection("roles").uniqueID("Guest").connection("users").uniqueID( user1Username ).post();
this.app().collection("roles").uniqueID("Guest").connection("users").uniqueID( user2Username ).post();
this.app().collection("roles").uniqueID("Guest").connection("users").uniqueID( user3Username ).post();
this.waitForQueueDrainAndRefreshIndex();
//8. post an activity to the group
//JSON should look like this:
//{'{"actor":{"displayName":"fred","uuid":"2b70e83a-8a3f-11e4-9716-235107bcadb1","username":"fred"},
// "verb":"post","content":"content"}'
Entity payload = new Entity();
payload.put("displayName", "fred");
payload.put("uuid", user1.get("uuid"));
payload.put("username", "fred");
Entity activity = new Entity();
activity.put("actor", payload);
activity.put("verb", "post");
final String content = "Wilma cannot see this!";
activity.put("content", content);
Entity activityResponse = this.app().collection("groups")
.uniqueID(groupName).connection("activities").post(activity);
assertEquals(activityResponse.get("content"), content);
this.waitForQueueDrainAndRefreshIndex();
//11. log user1 in, should then be using the app user's token not the admin token
this.getAppUserToken(user1Username, password);
//10. make sure the activity appears in the feed of user 1
Collection user1ActivityResponse = this.app().collection("groups")
.entity(group).connection("activities").get();
boolean found = false;
while (user1ActivityResponse.hasNext()) {
Entity tempActivity = user1ActivityResponse.next();
if (tempActivity.get("type").equals("activity") && tempActivity.get("content").equals(content)) {
found = true;
}
}
assertTrue(found);
//11. log user2 in, should then be using the app user's token not the admin token
this.getAppUserToken(user2Username, password);
//12. make sure the activity appears in the feed of user 2
Collection user2ActivityResponse = this.app().collection("groups")
.entity(group).connection("activities").get();
found = false;
while (user2ActivityResponse.hasNext()) {
Entity tempActivity = user2ActivityResponse.next();
if (tempActivity.get("type").equals("activity") && tempActivity.get("content").equals(content)) {
found = true;
}
}
assertTrue(found);
//13. log user3 in, should then be using the app user's token not the admin token
this.getAppUserToken(user3Username, password);
//14. user3 should be denied access to the group's activities
try {
Collection user3ActivityResponse = this.app().collection( "groups" )
.entity( group ).connection( "activities" ).get();
fail("user3 should have been denied access to the group's activities");
} catch (NotAuthorizedException expected) {}
//15. check that activity appears in user 1's personal feed
this.getAppUserToken(user1Username, password);
user1ActivityResponse = this.app().collection("activities").get();
found = false;
while (user1ActivityResponse.hasNext()) {
Entity tempActivity = user1ActivityResponse.next();
if (tempActivity.get("type").equals("activity") && tempActivity.get("content").equals(content)) {
found = true;
}
}
assertTrue(found);
//16. check that activity does not appear user 3's personal feed
this.getAppUserToken(user3Username, password);
Collection user3ActivityResponse = this.app().collection("activities").get();
found = false;
while (user3ActivityResponse.hasNext()) {
Entity tempActivity = user3ActivityResponse.next();
if (tempActivity.get("type").equals("activity") && tempActivity.get("content").equals("content")) {
found = true;
}
}
assertFalse(found);
}
public void updateGroupWithSameNameAsApp() throws IOException {
// create groupMap with same name as app
String groupPath = this.clientSetup.getAppName();
String groupName = this.clientSetup.getAppName();
String title = "Old Title";
Entity group = this.createGroup(groupName, groupPath, title);
String newTitle = "New Title";
group.put("title", newTitle);
Entity groupResponse = this.app().collection("groups").entity(group).put(group);
assertEquals(groupResponse.get("title"), newTitle);
this.waitForQueueDrainAndRefreshIndex();
// update that group by giving it a new title and using UUID in URL
String evenNewerTitle = "Even New Title";
group.put("title", newTitle);
String uuid = group.getAsString("uuid");
groupResponse = this.app().collection("groups").uniqueID(uuid).put(group);
assertEquals(groupResponse.get("title"), evenNewerTitle);
this.waitForQueueDrainAndRefreshIndex();
}
}