/*
* (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed 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.
*
* Contributors:
* dmetzler
*/
package org.nuxeo.ecm.restapi.test;
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 java.util.Arrays;
import javax.inject.Inject;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.nuxeo.ecm.core.api.NuxeoGroup;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.impl.NuxeoGroupImpl;
import org.nuxeo.ecm.core.test.annotations.Granularity;
import org.nuxeo.ecm.core.test.annotations.RepositoryConfig;
import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;
import org.nuxeo.runtime.test.runner.Jetty;
import org.nuxeo.runtime.transaction.TransactionHelper;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.core.util.MultivaluedMapImpl;
/**
* Tests the users and groups Rest endpoints
*
* @since 5.7.3
*/
@RunWith(FeaturesRunner.class)
@Features({ RestServerFeature.class })
@Jetty(port = 18090)
@RepositoryConfig(init = RestServerInit.class, cleanup = Granularity.METHOD)
public class UserGroupTest extends BaseUserTest {
@Inject
UserManager um;
protected void nextTransaction() {
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
}
@Test
public void itCanFetchAUser() throws Exception {
// Given the user1
// When I call the Rest endpoint
ClientResponse response = getResponse(RequestType.GET, "/user/user1");
// Then it returns the Json
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEqualsUser("user1", "John", "Lennon", node);
}
@Test
public void itReturnsA404OnNonExistentUser() throws Exception {
// Given a non existent user
// When I call the Rest endpoint
ClientResponse response = getResponse(RequestType.GET, "/user/nonexistentuser");
// Then it returns the Json
assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
}
@Test
public void itCanUpdateAUser() throws Exception {
// Given a modified user
NuxeoPrincipal user = um.getPrincipal("user1");
user.setFirstName("Paul");
user.setLastName("McCartney");
String userJson = getPrincipalAsJson(user);
// When I call a PUT on the Rest endpoint
ClientResponse response = getResponse(RequestType.PUT, "/user/user1", userJson);
// Then it changes the user
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEqualsUser("user1", "Paul", "McCartney", node);
nextTransaction(); // see committed changes
user = um.getPrincipal("user1");
assertEquals("Paul", user.getFirstName());
assertEquals("McCartney", user.getLastName());
}
@Test
public void itCanDeleteAUser() throws Exception {
// Given a modified user
NuxeoPrincipal user = um.getPrincipal("user1");
// When I call a DELETE on the Rest endpoint
ClientResponse response = getResponse(RequestType.DELETE, "/user/user1");
// Then the user is deleted
assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
nextTransaction(); // see committed changes
user = um.getPrincipal("user1");
assertNull(user);
}
@Test
public void itCanCreateAUser() throws Exception {
// Given a new user
NuxeoPrincipal principal = new NuxeoPrincipalImpl("newuser");
principal.setFirstName("test");
principal.setLastName("user");
principal.setCompany("nuxeo");
principal.setEmail("test@nuxeo.com");
// When i POST it on the user endpoint
ClientResponse response = getResponse(RequestType.POST, "/user", getPrincipalAsJson(principal));
// Then a user is created
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEqualsUser("newuser", "test", "user", node);
principal = um.getPrincipal("newuser");
assertEquals("test", principal.getFirstName());
assertEquals("user", principal.getLastName());
assertEquals("nuxeo", principal.getCompany());
assertEquals("test@nuxeo.com", principal.getEmail());
um.deleteUser("newuser");
assertNull(um.getPrincipal("newuser"));
}
@Test
public void itCanGetAGroup() throws Exception {
// Given a group
NuxeoGroup group = um.getGroup("group1");
// When i GET on the API
ClientResponse response = getResponse(RequestType.GET, "/group/" + group.getName());
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
// Then i GET the Group
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEqualsGroup(group.getName(), group.getLabel(), node);
}
@Test
public void itCanChangeAGroup() throws Exception {
// Given a modified group
NuxeoGroup group = um.getGroup("group1");
group.setLabel("modifiedGroup");
group.setMemberUsers(Arrays.asList(new String[] { "user1", "user2" }));
group.setMemberGroups(Arrays.asList(new String[] { "group2" }));
// When i PUT this group
ClientResponse response = getResponse(RequestType.PUT, "/group/" + group.getName(), getGroupAsJson(group));
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
// Then the group is modified server side
nextTransaction(); // see committed changes
group = um.getGroup("group1");
assertEquals("modifiedGroup", group.getLabel());
assertEquals(2, group.getMemberUsers().size());
assertEquals(1, group.getMemberGroups().size());
}
@Test
public void itCanDeleteGroup() throws Exception {
// When i DELETE on a group resources
ClientResponse response = getResponse(RequestType.DELETE, "/group/group1");
assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
// Then the group is deleted
assertNull(um.getGroup("group1"));
}
@Test
public void itCanCreateAGroup() throws Exception {
// Given a modified group
NuxeoGroup group = new NuxeoGroupImpl("newGroup");
group.setLabel("a new group");
group.setMemberUsers(Arrays.asList(new String[] { "user1", "user2" }));
group.setMemberGroups(Arrays.asList(new String[] { "group2" }));
// When i POST this group
ClientResponse response = getResponse(RequestType.POST, "/group/", getGroupAsJson(group));
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
// Then the group is modified server side
group = um.getGroup("newGroup");
assertEquals("a new group", group.getLabel());
assertEquals(2, group.getMemberUsers().size());
assertEquals(1, group.getMemberGroups().size());
um.deleteGroup("newGroup");
assertNull(um.getGroup("newGroup"));
}
@Test
public void itCanAddAGroupToAUser() throws Exception {
// Given a user and a group
NuxeoPrincipal principal = um.getPrincipal("user1");
NuxeoGroup group = um.getGroup("group2");
assertFalse(principal.isMemberOf(group.getName()));
// When i POST this group
ClientResponse response = getResponse(RequestType.POST,
"/user/" + principal.getName() + "/group/" + group.getName());
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
nextTransaction(); // see committed changes
principal = um.getPrincipal(principal.getName());
assertTrue(principal.isMemberOf(group.getName()));
}
@Test
public void itCanAddAUserToAGroup() throws Exception {
// Given a user and a group
NuxeoPrincipal principal = um.getPrincipal("user1");
NuxeoGroup group = um.getGroup("group2");
assertFalse(principal.isMemberOf(group.getName()));
// When i POST this group
ClientResponse response = getResponse(RequestType.POST,
"/group/" + group.getName() + "/user/" + principal.getName());
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
nextTransaction(); // see committed changes
principal = um.getPrincipal(principal.getName());
assertTrue(principal.isMemberOf(group.getName()));
}
@Test
public void itCanRemoveAUserToAGroup() throws Exception {
// Given a user in a group
NuxeoPrincipal principal = um.getPrincipal("user1");
NuxeoGroup group = um.getGroup("group1");
principal.setGroups(Arrays.asList(new String[] { group.getName() }));
um.updateUser(principal.getModel());
principal = um.getPrincipal("user1");
assertTrue(principal.isMemberOf(group.getName()));
// commit directory changes
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
// When i POST this group
ClientResponse response = getResponse(RequestType.DELETE,
"/user/" + principal.getName() + "/group/" + group.getName());
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
principal = um.getPrincipal(principal.getName());
assertFalse(principal.isMemberOf(group.getName()));
}
@Test
public void itCanSearchUsers() throws Exception {
// Given a search string
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.putSingle("q", "Steve");
ClientResponse response = getResponse(RequestType.GET, "/user/search", queryParams);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEquals("null", node.get("errorMessage").getValueAsText());
ArrayNode entries = (ArrayNode) node.get("entries");
assertEquals(1, entries.size());
assertEquals("user0", entries.get(0).get("id").getValueAsText());
}
@Test
public void itCanPaginateUsers() throws Exception {
String[][] expectedPages = new String[][] { new String[] { "Administrator", "Guest", "user0" },
new String[] { "user1", "user2", "user3" }, new String[] {"user4"} };
for (int i = 0; i < expectedPages.length; i++) {
JsonNode node = getResponseAsJson(RequestType.GET, "/user/search", getQueryParamsForPage(i));
assertPaging(i, 3, 3, 7, expectedPages[i].length, node);
assertUserEntries(node, expectedPages[i]);
}
}
@Test
public void itCanSearchGroups() throws Exception {
// Given a search string
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.putSingle("q", "Lannister");
ClientResponse response = getResponse(RequestType.GET, "/group/search", queryParams);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
JsonNode node = mapper.readTree(response.getEntityInputStream());
assertEquals("null", node.get("errorMessage").getValueAsText());
ArrayNode entries = (ArrayNode) node.get("entries");
assertEquals(1, entries.size());
assertEquals("Lannister", entries.get(0).get("grouplabel").getValueAsText());
}
@Test
public void itCanPaginateGroups() throws Exception {
String[][] expectedResults = new String[][] { new String[] { "administrators", "group0", "group1" },
new String[] { "group2", "group3", "members" }, new String[] { "powerusers" }, new String[0], };
for (int i = 0; i < expectedResults.length; i++) {
JsonNode node = getResponseAsJson(RequestType.GET, "/group/search", getQueryParamsForPage(i));
assertPaging(i, 3, 3, 7, expectedResults[i].length, node);
assertGroupEntries(node, expectedResults[i]);
}
}
/**
* @since 8.2
*/
@Test
public void itCanPaginateGroupMembers() throws Exception {
String[][] expectedResults = new String[][] { new String[] { "dummy", "dummy", "dummy" },
new String[] { "dummy" }};
for (int i = 0; i < expectedResults.length; i++) {
JsonNode node = getResponseAsJson(RequestType.GET, "/group/group1/@users", getQueryParamsForPage(i));
assertPaging(i, 3, 2, 4, expectedResults[i].length, node);
}
}
@Test
public void itDoesntWritePassword() throws Exception {
// When I call JSON for user1
JsonNode node = getResponseAsJson(RequestType.GET, "/user/user1");
// Then it doesn't contain the password at all
assertNull("", node.get("properties").get("password"));
}
/**
* @param node node to test
* @param strings an array of expected user names
* @since 5.8
*/
private void assertUserEntries(JsonNode node, String... users) {
ArrayNode entries = (ArrayNode) node.get("entries");
assertEquals(users.length, entries.size());
for (int i = 0; i < users.length; i++) {
assertEquals(users[i], entries.get(i).get("id").getValueAsText());
}
}
/**
* @param currentPageIndex expected currentPage index
* @param pageSize expected page size
* @param numberOfPage expected number of page
* @param resultsCount expected resultsCount
* @param currentPageSize expected currentPageSize
* @param jsonNodeToText
* @since 5.8
*/
private void assertPaging(int currentPageIndex, int pageSize, int numberOfPage, int resultsCount,
int currentPageSize, JsonNode node) {
assertTrue(node.get("isPaginable").getBooleanValue());
assertEquals(currentPageIndex, node.get("currentPageIndex").getIntValue());
assertEquals(pageSize, node.get("pageSize").getIntValue());
assertEquals(numberOfPage, node.get("numberOfPages").getIntValue());
assertEquals(resultsCount, node.get("resultsCount").getIntValue());
assertEquals(currentPageSize, node.get("currentPageSize").getIntValue());
}
/**
* @param pageIndex
* @return
* @since 5.8
*/
private MultivaluedMap<String, String> getQueryParamsForPage(int pageIndex) {
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.putSingle("q", "*");
queryParams.putSingle("currentPageIndex", Integer.toString(pageIndex));
queryParams.putSingle("pageSize", "3");
return queryParams;
}
/**
* @param node node to test
* @param strings an array of expected group names
* @since 5.8
*/
private void assertGroupEntries(JsonNode node, String... groups) {
ArrayNode entries = (ArrayNode) node.get("entries");
assertEquals(groups.length, entries.size());
for (int i = 0; i < groups.length; i++) {
assertEquals(groups[i], entries.get(i).get("groupname").getValueAsText());
}
}
}