diff --git a/docs/concepts.md b/docs/concepts.md index 3c4a3dcc..a31271bf 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -101,6 +101,13 @@ Application manager can update an application and the associated groups (add or Creating or deleting an application requires writer privileges. +### Application creator + +The application manager role is defined via the fr.insee.sugoi.api.regexp.role.application.creator property. + +Application creator are allowed to create a non existing application." + + ## Providers ### Realm config provider diff --git a/docs/configuration.md b/docs/configuration.md index 029cd033..eb462bf8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -152,6 +152,10 @@ To learn more about this feature see : [Notify external webservices](concepts.md | sugoi.api.event.webhook.mail.secondaryMailAttribute | Define the attribute where alternative mail for an user will be found | | secondaryMail | | sugoi.api.event.webhook.enabled.events | List of events that will be sent by webhook event listener. The only accepted values are SEND_LOGIN, REINIT_PASSWORD and CHANGE_PASSWORD | SEND_LOGIN,REINIT_PASSWORD,CHANGE_PASSWORD | SEND_LOGIN,REINIT_PASSWORD | +### Application manager configuration + +| fr.insee.sugoi.api.regexp.role.application.creator | Default pattern to use when searching an application creator in a realm. Realm should be passed as$(realm). | no default | ROLE_SUGOI_$(realm)_APPLICATION_CREATOR | + ### Spring actuator configuration Sugoi-api implements spring actuator documentation available here : [link](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html) diff --git a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/PermissionService.java b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/PermissionService.java index 420bcb2c..85184735 100644 --- a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/PermissionService.java +++ b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/PermissionService.java @@ -25,6 +25,8 @@ public interface PermissionService { public boolean isApplicationManager( SugoiUser sugoiUser, String realm, String userStorage, String application); + public boolean isApplicationCreator(SugoiUser sugoiUser, String realm, String userStorage); + public boolean isWriter(SugoiUser sugoiUser, String realm, String userStorage); public boolean isAdmin(SugoiUser sugoiUser); diff --git a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/ApplicationServiceImpl.java b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/ApplicationServiceImpl.java index 28005a4a..889a84a4 100644 --- a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/ApplicationServiceImpl.java +++ b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/ApplicationServiceImpl.java @@ -45,6 +45,7 @@ public class ApplicationServiceImpl implements ApplicationService { public ProviderResponse create( String realm, Application application, ProviderRequest providerRequest) { try { + ProviderResponse response = storeProvider.getWriterStore(realm).createApplication(application, providerRequest); sugoiEventPublisher.publishCustomEvent( @@ -56,6 +57,7 @@ public ProviderResponse create( && response.getStatus().equals(ProviderResponseStatus.OK)) { response.setEntity(findById(realm, application.getName())); } + return response; } catch (Exception e) { sugoiEventPublisher.publishCustomEvent( diff --git a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/PermissionServiceImpl.java b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/PermissionServiceImpl.java index 3f9dcaaf..e7660b23 100644 --- a/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/PermissionServiceImpl.java +++ b/sugoi-api-core/src/main/java/fr/insee/sugoi/core/service/impl/PermissionServiceImpl.java @@ -47,6 +47,9 @@ public class PermissionServiceImpl implements PermissionService { @Value("${fr.insee.sugoi.api.regexp.role.application.manager:}") private List applicationManagerRoleList; + @Value("${fr.insee.sugoi.api.regexp.role.application.creator:}") + private List regexpApplicationCreator; + public static final Logger logger = LoggerFactory.getLogger(PermissionServiceImpl.class); @Override @@ -57,6 +60,14 @@ public boolean isReader(SugoiUser sugoiUser, String realm, String userStorage) { || isWriter(sugoiUser, realm, userStorage); } + @Override + public boolean isApplicationCreator(SugoiUser sugoiUser, String realm, String userStorage) { + List searchRoleList = + getSearchRoleList(sugoiUser, realm, userStorage, null, regexpApplicationCreator); + return checkIfUserGetRoles(sugoiUser, searchRoleList) + || isWriter(sugoiUser, realm, userStorage); + } + @Override public boolean isPasswordManager(SugoiUser sugoiUser, String realm, String userStorage) { List searchRoleList = diff --git a/sugoi-api-core/src/test/java/fr/insee/sugoi/core/service/PermissionServiceTests.java b/sugoi-api-core/src/test/java/fr/insee/sugoi/core/service/PermissionServiceTests.java index c04a048b..ab1ae9a4 100644 --- a/sugoi-api-core/src/test/java/fr/insee/sugoi/core/service/PermissionServiceTests.java +++ b/sugoi-api-core/src/test/java/fr/insee/sugoi/core/service/PermissionServiceTests.java @@ -66,6 +66,21 @@ public void testReaderRegexp() { assertThat("User cannot read realm2", permissions.isReader(sugoiUser, "realm2", ""), is(false)); } + @Test + public void testApplicationCreatorRegexp() { + SugoiUser sugoiUser = + new SugoiUser("reader_realm1", List.of("ROLE_Application_creator_realm1")); + + assertThat( + "User is application creator realm1", + permissions.isApplicationCreator(sugoiUser, "realm1", ""), + is(true)); + assertThat( + "User is notapplication creator realm2", + permissions.isApplicationCreator(sugoiUser, "realm2", ""), + is(false)); + } + @Test public void testWriterRegexp() { SugoiUser sugoiUser = new SugoiUser("writer_realm1", List.of("role_Writer_realm1_sugoi")); diff --git a/sugoi-api-core/src/test/resources/permissions/test-regexp-permissions.properties b/sugoi-api-core/src/test/resources/permissions/test-regexp-permissions.properties index e9214247..26cd14dd 100644 --- a/sugoi-api-core/src/test/resources/permissions/test-regexp-permissions.properties +++ b/sugoi-api-core/src/test/resources/permissions/test-regexp-permissions.properties @@ -1,6 +1,7 @@ fr.insee.sugoi.api.regexp.role.writer=ROLE_WRITER_$(realm)_SUGOI fr.insee.sugoi.api.regexp.role.reader=ROLE_READER_$(realm)_SUGOI fr.insee.sugoi.api.regexp.role.admin=ROLE_ADMIN_SUGOI, ROLE_*_Admin +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(realm)_$(application), ROLE_ASI_$(application) diff --git a/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat1.properties b/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat1.properties index 14c901b1..cf898428 100644 --- a/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat1.properties +++ b/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat1.properties @@ -85,6 +85,7 @@ fr.insee.sugoi.api.regexp.role.reader=ROLE_SUGOI_$(realm)_READER,ROLE_SUGOI_$(re fr.insee.sugoi.api.regexp.role.writer=ROLE_SUGOI_$(realm)_WRITER,ROLE_SUGOI_$(realm)_$(userStorage)_WRITER fr.insee.sugoi.api.regexp.role.admin=ROLE_SUGOI_ADMIN,ROLE_ADMINISTRATEURS_SUGOI fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(application) +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) fr.insee.sugoi.api.enable.preauthorize=false management.endpoints.web.exposure.include=* diff --git a/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat2.properties b/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat2.properties index 5cf58d90..3cb98332 100644 --- a/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat2.properties +++ b/sugoi-api-distribution/sugoi-api-distribution-full-env/src/main/resources/tomcat-properties/tomcat2.properties @@ -81,6 +81,7 @@ fr.insee.sugoi.api.regexp.role.reader=ROLE_SUGOI_$(realm)_READER,ROLE_SUGOI_$(re fr.insee.sugoi.api.regexp.role.writer=ROLE_SUGOI_$(realm)_WRITER,ROLE_SUGOI_$(realm)_$(userStorage)_WRITER fr.insee.sugoi.api.regexp.role.admin=ROLE_SUGOI_ADMIN fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(application) +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) fr.insee.sugoi.api.enable.preauthorize=true management.endpoints.web.exposure.include=* diff --git a/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/controller/ApplicationController.java b/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/controller/ApplicationController.java index 93818a17..10747f23 100644 --- a/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/controller/ApplicationController.java +++ b/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/controller/ApplicationController.java @@ -142,7 +142,7 @@ public ResponseEntity> getApplications( description = "Application already exist", content = {@Content(mediaType = "application/json")}) }) - @PreAuthorize("@NewAuthorizeMethodDecider.isWriter(#realm,#storage)") + @PreAuthorize("@NewAuthorizeMethodDecider.isApplicationCreator(#realm,#storage)") public ResponseEntity createApplication( @Parameter( description = "Name of the realm where the operation will be made", diff --git a/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/decider/AuthorizeMethodDecider.java b/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/decider/AuthorizeMethodDecider.java index 39c1b81b..4dca1d7d 100644 --- a/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/decider/AuthorizeMethodDecider.java +++ b/sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/decider/AuthorizeMethodDecider.java @@ -71,6 +71,23 @@ public boolean isAppManager(String realm, String application) { return true; } + public boolean isApplicationCreator(String realm, String storage) { + if (enable) { + logger.info("Check if user is at least reader on realm {}", realm); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + List roles = + authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .map(String::toUpperCase) + .collect(Collectors.toList()); + SugoiUser sugoiUser = new SugoiUser(authentication.getName(), roles); + return permissionService.isApplicationCreator(sugoiUser, realm, storage) + || permissionService.isWriter(sugoiUser, realm, null); + } + logger.warn("PreAuthorize on request is disabled, can cause security problem"); + return true; + } + public boolean isPasswordManager(String realm, String userStorage) { if (enable) { logger.info( diff --git a/sugoi-api-rest-services/src/test/java/fr/insee/sugoi/services/controller/ApplicationControllerTest.java b/sugoi-api-rest-services/src/test/java/fr/insee/sugoi/services/controller/ApplicationControllerTest.java index 9becb448..1984c239 100644 --- a/sugoi-api-rest-services/src/test/java/fr/insee/sugoi/services/controller/ApplicationControllerTest.java +++ b/sugoi-api-rest-services/src/test/java/fr/insee/sugoi/services/controller/ApplicationControllerTest.java @@ -233,6 +233,37 @@ public void updateShouldCallUpdateServiceAndReturnNewApp() { } } + @Test + @WithMockUser + public void postShouldCallPostServiceAndReturnNewAppTrue() { + + try { + Mockito.doReturn( + new ProviderResponse("", "requestId", ProviderResponseStatus.OK, application1, null)) + .when(applicationService) + .create(Mockito.any(), Mockito.any(), Mockito.any()); + + RequestBuilder requestBuilder = + MockMvcRequestBuilders.post("/realms/domaine1/applications") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(application1)) + .param("addUser", "true") + .accept(MediaType.APPLICATION_JSON) + .with(csrf()); + + MockHttpServletResponse response = mockMvc.perform(requestBuilder).andReturn().getResponse(); + verify(applicationService).create(Mockito.any(), Mockito.any(), Mockito.any()); + assertThat( + "Should get new application", + objectMapper.readValue(response.getContentAsString(), Application.class).getName(), + is("SuperAppli")); + + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + @Test @WithMockUser public void postShouldCallPostServiceAndReturnNewApp() { diff --git a/sugoi-api-rest-services/src/test/resources/permissions/test-regexp-permissions.properties b/sugoi-api-rest-services/src/test/resources/permissions/test-regexp-permissions.properties index e9214247..dc0a4369 100644 --- a/sugoi-api-rest-services/src/test/resources/permissions/test-regexp-permissions.properties +++ b/sugoi-api-rest-services/src/test/resources/permissions/test-regexp-permissions.properties @@ -4,4 +4,5 @@ fr.insee.sugoi.api.regexp.role.admin=ROLE_ADMIN_SUGOI, ROLE_*_Admin fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(realm)_$(application), ROLE_ASI_$(application) +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) logging.level.fr.insee.sugoi=trace \ No newline at end of file diff --git a/sugoi-api-test/src/main/resources/tomcat-properties/tomcat1.properties b/sugoi-api-test/src/main/resources/tomcat-properties/tomcat1.properties index d0997236..0df0cd0f 100644 --- a/sugoi-api-test/src/main/resources/tomcat-properties/tomcat1.properties +++ b/sugoi-api-test/src/main/resources/tomcat-properties/tomcat1.properties @@ -83,6 +83,7 @@ fr.insee.sugoi.api.regexp.role.reader=ROLE_SUGOI_$(realm)_READER,ROLE_SUGOI_$(re fr.insee.sugoi.api.regexp.role.writer=ROLE_SUGOI_$(realm)_WRITER,ROLE_SUGOI_$(realm)_$(userStorage)_WRITER fr.insee.sugoi.api.regexp.role.admin=ROLE_SUGOI_ADMIN fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(application) +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) fr.insee.sugoi.api.enable.preauthorize=true fr.insee.sugoi.config.ldap.profils.pattern=cn=Profil_{realm}_WebServiceLdap diff --git a/sugoi-api-test/src/main/resources/tomcat-properties/tomcat2.properties b/sugoi-api-test/src/main/resources/tomcat-properties/tomcat2.properties index e6125a2a..cb1fe7f5 100644 --- a/sugoi-api-test/src/main/resources/tomcat-properties/tomcat2.properties +++ b/sugoi-api-test/src/main/resources/tomcat-properties/tomcat2.properties @@ -81,6 +81,7 @@ fr.insee.sugoi.api.regexp.role.reader=ROLE_SUGOI_$(realm)_READER,ROLE_SUGOI_$(re fr.insee.sugoi.api.regexp.role.writer=ROLE_SUGOI_$(realm)_WRITER,ROLE_SUGOI_$(realm)_$(userStorage)_WRITER fr.insee.sugoi.api.regexp.role.admin=ROLE_SUGOI_ADMIN fr.insee.sugoi.api.regexp.role.application.manager=ROLE_ASI_$(application) +fr.insee.sugoi.api.regexp.role.application.creator=ROLE_Application_creator_$(realm) fr.insee.sugoi.api.enable.preauthorize=true ## MAIL AND ID UNICITY