From 237f4ed7eb43aed222e12b3c1224797ec65fc5e3 Mon Sep 17 00:00:00 2001 From: "Matteo E. Minnai" Date: Wed, 26 Aug 2026 10:10:39 +0200 Subject: [PATCH] ESB-1231 cms/contentmodels controller access control relaxed --- .gitignore | 2 + .../ContentModelResourceController.java | 18 +++--- .../ContentModelControllerUnitTest.java | 58 ++++++++++++++++++- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index a774f6d738..2a76696db8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ target work *.tgz derby.log +/testing/ +/.testing/ diff --git a/cms-plugin/src/main/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelResourceController.java b/cms-plugin/src/main/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelResourceController.java index ebdab7ebb6..4c9eebc6c3 100644 --- a/cms-plugin/src/main/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelResourceController.java +++ b/cms-plugin/src/main/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelResourceController.java @@ -71,7 +71,7 @@ public ContentModelResourceController(ContentModelService contentModelService, } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getContentModels(RestListRequest requestList, BindingResult bindingResult) { @@ -84,7 +84,7 @@ public ResponseEntity> getContentModels(RestL } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(value = "/{modelId}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getContentModel(@PathVariable Long modelId) { logger.debug("loading contentModel {}", modelId); @@ -93,7 +93,7 @@ public ResponseEntity> getContentModel(@Path } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> addContentModel(@Valid @RequestBody ContentModelDto contentModel, BindingResult bindingResult) { logger.debug("adding content model"); @@ -105,7 +105,7 @@ public ResponseEntity> addContentModel(@Vali } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @PutMapping(value = "/{modelId}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> updateContentModel(@PathVariable Long modelId, @Valid @RequestBody ContentModelDto contentModel, BindingResult bindingResult) { logger.debug("updating contentModel {}", modelId); @@ -124,7 +124,7 @@ public ResponseEntity> updateContentModel(@P } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @DeleteMapping(value = "/{modelId}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> deleteContentModel(@PathVariable Long modelId) { logger.info("deleting content model {}", modelId); @@ -134,7 +134,7 @@ public ResponseEntity>> deleteContentMode } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(value = "/{modelId}/pagereferences", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getReferences(@PathVariable Long modelId,RestListRequest requestList) { logger.debug("loading contentModel references for model {}", modelId); @@ -144,7 +144,7 @@ public ResponseEntity> getReferences } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(value = "/{modelId}/usage", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getComponentUsage(@PathVariable Long modelId) { logger.debug("loading contentModel usage for model {}", modelId); @@ -153,7 +153,7 @@ public ResponseEntity> getComponentUsage(@Pat } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(value = "/{modelId}/usage/details", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getComponentUsageDetails(@PathVariable Long modelId, RestListRequest restListRequest) { logger.debug("get contentModel usage details for model {}", modelId); @@ -163,7 +163,7 @@ public ResponseEntity> getComponentUsage } @Override - @RestAccessControl(permission = Permission.SUPERUSER) + @RestAccessControl(permission = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) @GetMapping(value = "/dictionary", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getDictionary(@RequestParam(value = "typeCode", required = false) String typeCode) { logger.debug("loading contentModel dictionary for type {}", typeCode); diff --git a/cms-plugin/src/test/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelControllerUnitTest.java b/cms-plugin/src/test/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelControllerUnitTest.java index 34b0ddfd35..9fee8180ae 100644 --- a/cms-plugin/src/test/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelControllerUnitTest.java +++ b/cms-plugin/src/test/java/org/entando/entando/plugins/jacms/web/contentmodel/ContentModelControllerUnitTest.java @@ -12,6 +12,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.agiletec.aps.system.services.group.Group; +import com.agiletec.aps.system.services.role.Permission; import com.agiletec.aps.system.services.user.UserDetails; import com.agiletec.plugins.jacms.aps.system.services.content.model.Content; import com.agiletec.plugins.jacms.aps.system.services.contentmodel.dictionary.ContentModelDictionary; @@ -39,6 +41,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -224,6 +228,54 @@ public void shouldGetDictionary() throws Exception { result.andExpect(jsonPath("$.payload.$content").exists()); } + @ParameterizedTest + @ValueSource(strings = {Permission.SUPERUSER, Permission.CONTENT_EDITOR, Permission.CONTENT_SUPERVISOR}) + public void shouldAllowAccessToEveryEndpointForRelaxedPermissions(String permission) throws Exception { + UserDetails user = createUserWithPermission(permission); + ContentModelDto contentModelDto = buildContentModelDto(1l, "AAA"); + + performRequest(get(BASE_URI), null, user).andExpect(status().isOk()); + performRequest(get(BASE_URI + "/{id}", 1), null, user).andExpect(status().isOk()); + performRequest(post(BASE_URI), mapper.writeValueAsString(contentModelDto), user).andExpect(status().isOk()); + performRequest(put(BASE_URI + "/{id}", 1), mapper.writeValueAsString(contentModelDto), user).andExpect(status().isOk()); + performRequest(get(BASE_URI + "/{id}/pagereferences", 1), null, user).andExpect(status().isOk()); + performRequest(get(BASE_URI + "/{id}/usage", 1), null, user).andExpect(status().isOk()); + performRequest(get(BASE_URI + "/{id}/usage/details", 1), null, user).andExpect(status().isOk()); + performRequest(get(BASE_URI + "/dictionary"), null, user).andExpect(status().isOk()); + performRequest(delete(BASE_URI + "/{id}", 1), null, user).andExpect(status().isOk()); + } + + @Test + public void shouldForbidAccessToEveryEndpointForUnrelatedPermission() throws Exception { + UserDetails user = createUserWithPermission(Permission.MANAGE_PAGES); + ContentModelDto contentModelDto = buildContentModelDto(1l, "AAA"); + + performRequest(get(BASE_URI), null, user).andExpect(status().isForbidden()); + performRequest(get(BASE_URI + "/{id}", 1), null, user).andExpect(status().isForbidden()); + performRequest(post(BASE_URI), mapper.writeValueAsString(contentModelDto), user).andExpect(status().isForbidden()); + performRequest(put(BASE_URI + "/{id}", 1), mapper.writeValueAsString(contentModelDto), user).andExpect(status().isForbidden()); + performRequest(get(BASE_URI + "/{id}/pagereferences", 1), null, user).andExpect(status().isForbidden()); + performRequest(get(BASE_URI + "/{id}/usage", 1), null, user).andExpect(status().isForbidden()); + performRequest(get(BASE_URI + "/{id}/usage/details", 1), null, user).andExpect(status().isForbidden()); + performRequest(get(BASE_URI + "/dictionary"), null, user).andExpect(status().isForbidden()); + performRequest(delete(BASE_URI + "/{id}", 1), null, user).andExpect(status().isForbidden()); + } + + private UserDetails createUserWithPermission(String permission) { + return new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24") + .withAuthorization(Group.FREE_GROUP_NAME, "roletest", permission) + .build(); + } + + private ContentModelDto buildContentModelDto(long id, String contentType) { + ContentModelDto contentModelDto = new ContentModelDto(); + contentModelDto.setId(id); + contentModelDto.setContentType(contentType); + contentModelDto.setContentShape("contentShape"); + contentModelDto.setDescr("description"); + return contentModelDto; + } + private ResultActions performRequest(MockHttpServletRequestBuilder requestBuilder, ContentModelDto contentModelDto) throws Exception { String payload = mapper.writeValueAsString(contentModelDto); return performRequest(requestBuilder, payload); @@ -234,8 +286,12 @@ private ResultActions performRequest(MockHttpServletRequestBuilder requestBuilde } private ResultActions performRequest(MockHttpServletRequestBuilder requestBuilder, String payload) throws Exception { - UserDetails user = new OAuth2TestUtils.UserBuilder("jack_bauer", "0x24").grantedToRoleAdmin().build(); + return performRequest(requestBuilder, payload, user); + } + + private ResultActions performRequest(MockHttpServletRequestBuilder requestBuilder, String payload, UserDetails user) throws Exception { + String accessToken = mockOAuthInterceptor(user); requestBuilder = requestBuilder