diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java index 9863ed0ca2..02c7f4fe46 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RestResourceController.java @@ -51,6 +51,7 @@ import org.dspace.app.rest.repository.LinkRestRepository; import org.dspace.app.rest.utils.RestRepositoryUtils; import org.dspace.app.rest.utils.Utils; import org.dspace.authorize.AuthorizeException; +import org.dspace.util.UUIDUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -441,7 +442,15 @@ public class RestResourceController implements InitializingBean { throws HttpRequestMethodNotSupportedException { checkModelPluralForm(apiCategory, model); DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model); - RestAddressableModel modelObject = repository.createAndReturn(); + + String parentCommunityString = request.getParameter("parent"); + RestAddressableModel modelObject; + if (parentCommunityString != null) { + UUID parentCommunityUuid = UUIDUtils.fromString(parentCommunityString); + modelObject = repository.createAndReturn(parentCommunityUuid); + } else { + modelObject = repository.createAndReturn(); + } if (modelObject == null) { return ControllerUtils.toEmptyResponse(HttpStatus.CREATED); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java index cceb32b546..c09178eae6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/CollectionRestRepository.java @@ -17,7 +17,6 @@ import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.converter.CollectionConverter; @@ -38,7 +37,6 @@ import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.core.Constants; import org.dspace.core.Context; -import org.dspace.util.UUIDUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -169,8 +167,19 @@ public class CollectionRestRepository extends DSpaceObjectRestRepository wrapResource(T model, String... rels); + /** + * Create and return a new instance. Data are usually retrieved from the thread bound http request + * + * @return the created REST object + */ + public T createAndReturn() { + Context context = null; + try { + context = obtainContext(); + T entity = thisRepository.createAndReturn(context); + context.commit(); + return entity; + } catch (AuthorizeException e) { + throw new RESTAuthorizationException(e); + } catch (SQLException ex) { + throw new RuntimeException(ex.getMessage(), ex); + } + } + + /** + * Create and return a new instance after adding to the parent. Data are usually retrieved from + * the thread bound http request. + * + * @param uuid the id of the parent object + * @return the created REST object + */ + public T createAndReturn(UUID uuid) { + Context context = null; + try { + context = obtainContext(); + T entity = thisRepository.createAndReturn(context, uuid); + context.commit(); + return entity; + } catch (AuthorizeException e) { + throw new RESTAuthorizationException(e); + } catch (SQLException ex) { + throw new RuntimeException(ex.getMessage(), ex); + } + } + /** * Create and return a new instance. Data is recovered from the thread bound HTTP request and the list * of DSpaceObjects provided in the uri-list body @@ -281,22 +322,22 @@ public abstract class DSpaceRestRepositorySome cool HTML code here

")) - .put("dc.description.abstract", - new MetadataValueRest("Sample top-level community created via the REST API")) - .put("dc.description.tableofcontents", - new MetadataValueRest("

HTML News

")) - .put("dc.rights", - new MetadataValueRest("Custom Copyright Text")) - .put("dc.title", - new MetadataValueRest("Title Text"))); + // Anonymous user tries to create a community. + // Should fail because user is not authenticated. Error 401. + getClient().perform(post("/api/core/communities") + .content(mapper.writeValueAsBytes(comm)) + .param("parent", parentCommunity.getID().toString()) + .contentType(contentType)) + .andExpect(status().isUnauthorized()); - String authToken = getAuthToken(admin.getEmail(), password); - // Capture the UUID of the created Community (see andDo() below) + // Non-admin Eperson tries to create a community. + // Should fail because user doesn't have permissions. Error 403. + String authToken = getAuthToken(eperson.getEmail(), password); + getClient(authToken).perform(post("/api/core/communities") + .content(mapper.writeValueAsBytes(comm)) + .param("parent", parentCommunity.getID().toString()) + .contentType(contentType)) + .andExpect(status().isForbidden()); + } + + @Test + public void createSubCommunityAuthorizedTest() throws Exception { + //We turn off the authorization system in order to create the structure as defined below + context.turnOffAuthorisationSystem(); + + // Create a parent community to POST a new sub-community to + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + // ADD authorization on parent community + context.setCurrentUser(eperson); + authorizeService.addPolicy(context, parentCommunity, Constants.ADD, eperson); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + ObjectMapper mapper = new ObjectMapper(); + CommunityRest comm = new CommunityRest(); + // We send a name but the created community should set this to the title + comm.setName("Test Sub-Level Community"); + + comm.setMetadata(new MetadataRest() + .put("dc.description", + new MetadataValueRest("

Some cool HTML code here

")) + .put("dc.description.abstract", + new MetadataValueRest("Sample top-level community created via the REST API")) + .put("dc.description.tableofcontents", + new MetadataValueRest("

HTML News

")) + .put("dc.rights", + new MetadataValueRest("Custom Copyright Text")) + .put("dc.title", + new MetadataValueRest("Title Text"))); + + // Capture the UUID of the created Community (see andDo() below) AtomicReference idRef = new AtomicReference(); try { getClient(authToken).perform(post("/api/core/communities") - .content(mapper.writeValueAsBytes(comm)) - .param("parent", parentCommunity.getID().toString()) - .contentType(contentType)) + .content(mapper.writeValueAsBytes(comm)) + .param("parent", parentCommunity.getID().toString()) + .contentType(contentType)) .andExpect(status().isCreated()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.allOf( @@ -181,15 +222,15 @@ public class CommunityRestRepositoryIT extends AbstractControllerIntegrationTest hasJsonPath("$._links.self.href", not(empty())), hasJsonPath("$.metadata", Matchers.allOf( MetadataMatcher.matchMetadata("dc.description", - "

Some cool HTML code here

"), + "

Some cool HTML code here

"), MetadataMatcher.matchMetadata("dc.description.abstract", - "Sample top-level community created via the REST API"), + "Sample top-level community created via the REST API"), MetadataMatcher.matchMetadata("dc.description.tableofcontents", - "

HTML News

"), + "

HTML News

"), MetadataMatcher.matchMetadata("dc.rights", - "Custom Copyright Text"), + "Custom Copyright Text"), MetadataMatcher.matchMetadata("dc.title", - "Title Text") + "Title Text") ) ) )))