diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java index be5046077..e009fa974 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java +++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIV2Controller.java @@ -167,10 +167,10 @@ private void setBrAPIServerInfo(BrAPIServerInfo serverInfo) { serverInfo.setDocumentationURL("https://brapi.org/specification"); } - // Explicit match for /seasons GET endpoint, to allow Experimental Collaborator access. + // Explicit match for the /seasons GET pass-through endpoint. @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/seasons{?queryParams}") @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN, ProgramSecuredRole.PROGRAM_ADMIN, ProgramSecuredRole.READ_ONLY, ProgramSecuredRole.EXPERIMENTAL_COLLABORATOR}) + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) public HttpResponse getSeasons(@PathVariable("programId") UUID programId, HttpRequest request, @PathVariable Optional queryParams) { String path = "seasons"; if (queryParams.isPresent()) { @@ -181,7 +181,7 @@ public HttpResponse getSeasons(@PathVariable("programId") UUID programId, Htt @Get("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/{+path}") @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) public HttpResponse getCatchall(@PathVariable("path") String path, @PathVariable("programId") UUID programId, HttpRequest request) { return executeRequest(path, programId, request, "GET"); } @@ -189,7 +189,7 @@ public HttpResponse getCatchall(@PathVariable("path") String path, @PathVaria @Post("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/{+path}") @Consumes(MediaType.ALL) @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) public HttpResponse postCatchall(@PathVariable("path") String path, @PathVariable("programId") UUID programId, HttpRequest request, @Header("Content-Type") String contentType) { return executeByteRequest(path, programId, request, contentType, "POST"); @@ -198,7 +198,7 @@ public HttpResponse postCatchall(@PathVariable("path") String path, @Pat @Put("/${micronaut.bi.api.version}/programs/{programId}" + BrapiVersion.BRAPI_V2 + "/{+path}") @Consumes(MediaType.ALL) @Produces(MediaType.APPLICATION_JSON) - @ProgramSecured(roleGroups = {ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES}) + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) public HttpResponse putCatchall(@PathVariable("path") String path, @PathVariable("programId") UUID programId, HttpRequest request, @Header("Content-Type") String contentType) { return executeByteRequest(path, programId, request, contentType, "PUT"); diff --git a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java index 24868f3fe..e77b64b94 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/BrAPIV2ControllerIntegrationTest.java @@ -23,6 +23,7 @@ import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; import io.micronaut.http.MediaType; +import io.micronaut.http.MutableHttpRequest; import io.micronaut.http.client.RxHttpClient; import io.micronaut.http.client.annotation.Client; import io.micronaut.http.client.exceptions.HttpClientResponseException; @@ -51,6 +52,7 @@ import static io.micronaut.http.HttpRequest.GET; import static io.micronaut.http.HttpRequest.POST; +import static io.micronaut.http.HttpRequest.PUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -171,20 +173,43 @@ public void testPostVariablesNotFound() { public void testPutVariablesNotFound() { BrAPIObservationVariable variable = generateVariable(); - Flowable> postCall = biClient.exchange( - POST(String.format("%s/programs/%s/brapi/v2/variables", - biApiVersion, - validProgram.getId().toString()), Arrays.asList(variable)) + Flowable> putCall = biClient.exchange( + PUT(String.format("%s/programs/%s/brapi/v2/variables", + biApiVersion, + validProgram.getId().toString()), Arrays.asList(variable)) .contentType(MediaType.APPLICATION_JSON) .bearerAuth("test-registered-user"), String.class ); HttpClientResponseException e = Assertions.assertThrows(HttpClientResponseException.class, () -> { - HttpResponse response = postCall.blockingFirst(); + HttpResponse response = putCall.blockingFirst(); }); assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); } + @Test + public void testProgramUserCannotAccessPassThroughEndpoints() { + String brapiBaseUrl = String.format("%s/programs/%s/brapi/v2", + biApiVersion, + validProgram.getId()); + + assertProgramUserForbidden(GET(brapiBaseUrl + "/seasons")); + assertProgramUserForbidden(GET(brapiBaseUrl + "/unimplemented-get")); + assertProgramUserForbidden(POST(brapiBaseUrl + "/unimplemented-post", "{}") + .contentType(MediaType.APPLICATION_JSON)); + assertProgramUserForbidden(PUT(brapiBaseUrl + "/unimplemented-put", "{}") + .contentType(MediaType.APPLICATION_JSON)); + } + + private void assertProgramUserForbidden(MutableHttpRequest request) { + Flowable> call = biClient.exchange( + request.bearerAuth("other-registered-user"), String.class); + + HttpClientResponseException exception = Assertions.assertThrows( + HttpClientResponseException.class, call::blockingFirst); + assertEquals(HttpStatus.FORBIDDEN, exception.getStatus()); + } + private BrAPIObservationVariable generateVariable() {