diff --git a/server/src/main/java/access/api/UserController.java b/server/src/main/java/access/api/UserController.java index 31304b0b..b5f52bef 100644 --- a/server/src/main/java/access/api/UserController.java +++ b/server/src/main/java/access/api/UserController.java @@ -10,6 +10,7 @@ import access.provision.graph.GraphClient; import access.repository.InvitationRepository; import access.repository.RemoteProvisionedUserRepository; +import access.repository.RoleRepository; import access.repository.UserRepository; import access.security.UserPermissions; import com.fasterxml.jackson.core.JsonProcessingException; @@ -60,6 +61,9 @@ public class UserController { private final ObjectMapper objectMapper; private final RemoteProvisionedUserRepository remoteProvisionedUserRepository; private final GraphClient graphClient; + private final boolean limitInstitutionAdminRoleVisibility; + private final RoleRepository roleRepository; + @Autowired public UserController(Config config, @@ -71,14 +75,17 @@ public UserController(Config config, KeyStore keyStore, @Value("${config.eduid-idp-schac-home-organization}") String eduidIdpSchacHomeOrganization, @Value("${config.server-url}") String serverBaseURL, - @Value("${voot.group_urn_domain}") String groupUrnPrefix) { + @Value("${voot.group_urn_domain}") String groupUrnPrefix, + @Value("${feature.limit-institution-admin-role-visibility}") boolean limitInstitutionAdminRoleVisibility, RoleRepository roleRepository) { this.invitationRepository = invitationRepository; this.config = config.withGroupUrnPrefix(groupUrnPrefix); this.userRepository = userRepository; this.objectMapper = objectMapper; this.manage = manage; this.remoteProvisionedUserRepository = remoteProvisionedUserRepository; + this.limitInstitutionAdminRoleVisibility = limitInstitutionAdminRoleVisibility; this.graphClient = new GraphClient(serverBaseURL, eduidIdpSchacHomeOrganization, keyStore, objectMapper); + this.roleRepository = roleRepository; } @GetMapping("config") @@ -140,7 +147,17 @@ public ResponseEntity> searchByApplication(@RequestParam(value = LOG.debug(String.format("/searchByApplication for user %s and query %s", user.getEduPersonPrincipalName(), query)); UserPermissions.assertInstitutionAdmin(user); - List manageIdentifiers = user.getApplications().stream().map(application -> (String) application.get("id")).collect(Collectors.toList()); + List manageIdentifiers; + if (limitInstitutionAdminRoleVisibility) { + manageIdentifiers = roleRepository.findByOrganizationGUID(user.getOrganizationGUID()) + .stream() + .map(role -> role.getApplicationUsages()) + .flatMap(Set::stream) + .map(applicationUsage -> applicationUsage.getApplication().getManageId()) + .toList(); + } else { + manageIdentifiers = user.getApplications().stream().map(application -> (String) application.get("id")).collect(Collectors.toList()); + } List> results = query.equals("owl") ? userRepository.searchByApplicationAllUsers(manageIdentifiers) : userRepository.searchByApplication(manageIdentifiers, query.replaceAll("@", " ") + "*", 15); diff --git a/server/src/test/java/access/api/UserControllerNoLimitInstitutionAdminVisibilityTest.java b/server/src/test/java/access/api/UserControllerNoLimitInstitutionAdminVisibilityTest.java new file mode 100644 index 00000000..9185ff00 --- /dev/null +++ b/server/src/test/java/access/api/UserControllerNoLimitInstitutionAdminVisibilityTest.java @@ -0,0 +1,83 @@ +package access.api; + +import access.AbstractTest; +import access.AccessCookieFilter; +import access.exception.NotFoundException; +import access.manage.EntityType; +import access.model.Authority; +import access.model.RemoteProvisionedUser; +import access.model.User; +import access.model.UserRole; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import io.restassured.common.mapper.TypeRef; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Stream; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.*; +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "oidcng.introspect-url=http://localhost:8081/introspect", + "config.past-date-allowed=False", + "spring.security.oauth2.client.provider.oidcng.authorization-uri=http://localhost:8081/authorization", + "spring.security.oauth2.client.provider.oidcng.token-uri=http://localhost:8081/token", + "spring.security.oauth2.client.provider.oidcng.user-info-uri=http://localhost:8081/user-info", + "spring.security.oauth2.client.provider.oidcng.jwk-set-uri=http://localhost:8081/jwk-set", + "manage.url: http://localhost:8081", + "manage.enabled: true", + "feature.limit-institution-admin-role-visibility=false" + }) +@SuppressWarnings("unchecked") +class UserControllerNoLimitInstitutionAdminVisibilityTest extends AbstractTest { + + @Test + void searchByApplication() throws Exception { + //Institution admin is enriched with Manage information + super.stubForManageProvidersAllowedByIdP(ORGANISATION_GUID); + AccessCookieFilter accessCookieFilter = openIDConnectFlow("/api/v1/users/login", INSTITUTION_ADMIN_SUB); + + List> users = given() + .when() + .filter(accessCookieFilter.cookieFilter()) + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .queryParam("query", "Doe") + .get("/api/v1/users/search-by-application") + .as(new TypeRef<>() { + }); + assertEquals(3, users.size()); + } + + @Test + void searchAllUsersByApplication() throws Exception { + //Institution admin is enriched with Manage information + super.stubForManageProvidersAllowedByIdP(ORGANISATION_GUID); + AccessCookieFilter accessCookieFilter = openIDConnectFlow("/api/v1/users/login", INSTITUTION_ADMIN_SUB); + + List> users = given() + .when() + .filter(accessCookieFilter.cookieFilter()) + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .queryParam("query", "owl") + .get("/api/v1/users/search-by-application") + .as(new TypeRef<>() { + }); + assertEquals(3, users.size()); + } + +} \ No newline at end of file diff --git a/server/src/test/java/access/api/UserControllerTest.java b/server/src/test/java/access/api/UserControllerTest.java index c6dafda9..7055244e 100644 --- a/server/src/test/java/access/api/UserControllerTest.java +++ b/server/src/test/java/access/api/UserControllerTest.java @@ -309,7 +309,7 @@ void searchByApplication() throws Exception { .get("/api/v1/users/search-by-application") .as(new TypeRef<>() { }); - assertEquals(3, users.size()); + assertEquals(1, users.size()); } @Test @@ -327,7 +327,7 @@ void searchAllUsersByApplication() throws Exception { .get("/api/v1/users/search-by-application") .as(new TypeRef<>() { }); - assertEquals(3, users.size()); + assertEquals(1, users.size()); } @Test