Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Allow anyone to create an application #769

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

il ne faut pas créer un nouveau paragraphe mais la mettre avec les configs similaire (par ex ligne 117)


### 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class ApplicationServiceImpl implements ApplicationService {
public ProviderResponse create(
String realm, Application application, ProviderRequest providerRequest) {
try {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

est-ce que tu peux revert cette modif pour éviter d'avoir un changement inutile sur le fichier ?

ProviderResponse response =
storeProvider.getWriterStore(realm).createApplication(application, providerRequest);
sugoiEventPublisher.publishCustomEvent(
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class PermissionServiceImpl implements PermissionService {
@Value("${fr.insee.sugoi.api.regexp.role.application.manager:}")
private List<String> applicationManagerRoleList;

@Value("${fr.insee.sugoi.api.regexp.role.application.creator:}")
private List<String> regexpApplicationCreator;

public static final Logger logger = LoggerFactory.getLogger(PermissionServiceImpl.class);

@Override
Expand All @@ -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<String> 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<String> searchRoleList =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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=*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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=*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public ResponseEntity<PageResult<Application>> getApplications(
description = "Application already exist",
content = {@Content(mediaType = "application/json")})
})
@PreAuthorize("@NewAuthorizeMethodDecider.isWriter(#realm,#storage)")
@PreAuthorize("@NewAuthorizeMethodDecider.isApplicationCreator(#realm,#storage)")
public ResponseEntity<Application> createApplication(
@Parameter(
description = "Name of the realm where the operation will be made",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down