From 1a95ce6755b69f60a0488b097d7bb477efa8d581 Mon Sep 17 00:00:00 2001 From: Martin Lowe <martin.lowe@eclipse-foundation.org> Date: Fri, 14 Mar 2025 14:25:05 -0400 Subject: [PATCH 1/2] feat(oidc): Switch API from Drupal OAuth to use Keycloak in its place As part of the migration away from drupal auth, openvsx is one of the 3 remaining services that use the oauth server in the API stack. With only 1 main consumer, this should be a relatively simple switch over. --- pom.xml | 19 ++- .../openvsx/resources/ProfileResource.java | 35 +++- .../resources/PublisherAgreementResource.java | 149 ++++++++++++------ src/main/resources/application.properties | 7 +- .../resources/ProfileResourceTest.java | 28 ++-- .../PublisherAgreementResourceTest.java | 46 ++++-- .../openvsx/test/api/MockDrupalOAuthAPI.java | 116 -------------- src/test/resources/application.properties | 13 +- 8 files changed, 187 insertions(+), 226 deletions(-) delete mode 100644 src/test/java/org/eclipsefoundation/openvsx/test/api/MockDrupalOAuthAPI.java diff --git a/pom.xml b/pom.xml index 97e00cc..29030b8 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-rest-client-oidc-filter</artifactId> + <artifactId>quarkus-oidc-client</artifactId> </dependency> <!-- Testing dependencies only --> @@ -97,6 +97,21 @@ <version>${eclipse-api-version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-test-security</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-test-security-oidc</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-test-oidc-server</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> @@ -182,4 +197,4 @@ </properties> </profile> </profiles> -</project> \ No newline at end of file +</project> diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java index eb424ae..659af96 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java @@ -12,24 +12,43 @@ package org.eclipsefoundation.openvsx.resources; import java.util.Arrays; -import java.util.List; -import org.eclipsefoundation.efservices.api.models.EfUser; -import org.eclipsefoundation.efservices.models.AuthenticatedRequestWrapper; +import org.eclipsefoundation.efservices.services.ProfileService; +import org.eclipsefoundation.http.exception.ApplicationException; -import jakarta.inject.Inject; +import io.quarkus.security.Authenticated; +import io.quarkus.security.identity.SecurityIdentity; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +@Authenticated @Path("profile") public class ProfileResource { - @Inject - AuthenticatedRequestWrapper userProfile; + private final ProfileService profile; + private final SecurityIdentity ident; + + /** + * Default constructor, taking in the required services to function. + * + * @param profile service that retrieves EF user profiles by username + * @param ident logged in OIDC user + */ + public ProfileResource(ProfileService profile, SecurityIdentity ident) { + this.profile = profile; + this.ident = ident; + } @GET - public List<EfUser> getProfileInfo() { + public Response getProfileInfo() { // Returns the public profile data tied to the current user - return Arrays.asList(userProfile.getCurrentUser().getPublicProfile()); + return Response + .ok(Arrays + .asList(profile + .fetchUserByUsername(ident.getPrincipal().getName(), true) + .orElseThrow(() -> new ApplicationException("No user profile found for logged in user", 500)) + .getPublicProfile())) + .build(); } } diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java index cfff5a7..1c93e27 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java @@ -15,9 +15,9 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.eclipsefoundation.efservices.api.models.EfUser; -import org.eclipsefoundation.efservices.models.AuthenticatedRequestWrapper; +import org.eclipsefoundation.efservices.services.ProfileService; import org.eclipsefoundation.foundationdb.client.runtime.model.people.PeopleDocumentData; -import org.eclipsefoundation.http.model.WebError; +import org.eclipsefoundation.http.exception.ApplicationException; import org.eclipsefoundation.openvsx.config.PublisherAgreementConfig; import org.eclipsefoundation.openvsx.models.AgreementSigningRequest; import org.eclipsefoundation.openvsx.models.PublisherAgreementData; @@ -25,7 +25,8 @@ import org.eclipsefoundation.openvsx.services.FoundationOperationService; import org.eclipsefoundation.openvsx.services.PublisherAgreementService; import org.eclipsefoundation.utils.exception.FinalForbiddenException; -import jakarta.inject.Inject; +import io.quarkus.security.Authenticated; +import io.quarkus.security.identity.SecurityIdentity; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -37,51 +38,70 @@ import jakarta.ws.rs.ServerErrorException; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; +/** + * Resource containing calls for retrieving and signing OpenVSX publisher agreements. + */ +@Authenticated @Path("publisher_agreement") public class PublisherAgreementResource { private static final String NOT_FOUND_MSG_FORMAT = "Unable to find agreement for user: %s"; - @Inject - PublisherAgreementConfig config; - - @Inject - AuthenticatedRequestWrapper userProfile; - - @Inject - PublisherAgreementService agreementService; - - @Inject - FoundationOperationService foundationService; + private final PublisherAgreementConfig config; + private final PublisherAgreementService agreementService; + private final FoundationOperationService foundationService; + private final ProfileService profile; + private final SecurityIdentity ident; + + public PublisherAgreementResource(PublisherAgreementConfig config, PublisherAgreementService agreementService, + FoundationOperationService foundationService, ProfileService profile, SecurityIdentity ident) { + this.config = config; + this.agreementService = agreementService; + this.foundationService = foundationService; + this.profile = profile; + this.ident = ident; + } + /** + * Retrieve the currently active publisher agreement if one exists. + * + * @return the publisher agreement if it exists + * @throws NotFoundException when there is no agreement in place for the current user + */ @GET - public Response getAgreement() { - - // Uses currently logged in user. Only an onwer can fetch their agreement - String username = userProfile.getCurrentUser().name(); - - // Fetch agreement for user - Optional<PublisherAgreementData> result = agreementService.getPublisherAgreementByUsername(username); - if (result.isEmpty()) { - throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); - } - - return Response.ok(result).build(); + public PublisherAgreementData getAgreement() { + // Uses currently logged in user. Only an owner can fetch their agreement + String username = ident.getPrincipal().getName(); + + // Fetch agreement for user, throwing a 404 exception if there is no active agreement + return agreementService + .getPublisherAgreementByUsername(username) + .orElseThrow(() -> new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username))); } + /** + * Sign a new OpenVSX publisher agreement for the currently logged in user. A new agreement will only be signed and persisted if there + * isn't already an active agreement for the user. + * + * @param body the request containing the version and GitHub handle of the user + * @return the newly signed agreement. + * @throws BadRequestException if the passed agreement parameters do not match the configured allowed version, if the logged in user + * doesn't match the passed GitHub handle, or if the agreement cannot be created. + * @throws ApplicationException if the user in question already has an active agreement + * @throws ServerErrorException if a foundation database personal record cannot be found or created for the active user + */ @POST - public Response createAgreement(AgreementSigningRequest body) { - + public PublisherAgreementData createAgreement(AgreementSigningRequest body) { // Uses currently logged in user. Only an owner can create their agreement - EfUser user = userProfile.getCurrentUser(); + EfUser user = getLoggedInUser(); // Check if body format is correct validateSigningRequest(body); // Conflict if already signed agreement that isn't expired if (agreementService.getPublisherAgreementByUsername(user.name()).isPresent()) { - return new WebError(Status.CONFLICT, - "The request could not be completed due to a conflict with the current state of the resource.").asResponse(); + throw new ApplicationException("The request could not be completed due to a conflict with the current state of the resource.", + Status.CONFLICT.getStatusCode()); } // Create user in fdndb if they don't exist @@ -89,29 +109,40 @@ public class PublisherAgreementResource { throw new ServerErrorException("Internal Server Error", Status.INTERNAL_SERVER_ERROR); } - // Attempt to persist the agreement data. Returning the result - Optional<PublisherAgreementData> result = agreementService.createPublisherAgreement(user); - if (result.isEmpty()) { - throw new BadRequestException("Unable to create Publisher Agreement with current request parameters"); - } - return Response.ok(result.get()).build(); + // Attempt to persist the agreement data, returning the new agreement on success + return agreementService + .createPublisherAgreement(user) + .orElseThrow(() -> new BadRequestException("Unable to create Publisher Agreement with current request parameters")); } + /** + * Retrieve the active publisher agreement for the listed user. + * + * @return the publisher agreement if it exists + * @throws NotFoundException when there is no agreement in place for the indicated user + * @throws FinalForbiddenException if the username doesn't match the logged in user, and the logged in user isn't an admin + */ @GET @Path("{efUsername}") - public Response getAgreementForUser(@PathParam("efUsername") String username) { - + public PublisherAgreementData getAgreementForUser(@PathParam("efUsername") String username) { // The owner or admin can retrieve a specific agreement checkIfAdminOrSelf(username); - Optional<PublisherAgreementData> result = agreementService.getPublisherAgreementByUsername(username); - if (result.isEmpty()) { - throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); - } - - return Response.ok(result).build(); + // Fetch agreement for user, throwing a 404 exception if there is no active agreement + return agreementService + .getPublisherAgreementByUsername(username) + .orElseThrow(() -> new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username))); } + /** + * Revokes an active agreement for the stated user. Can only be triggered by the target user, or by an admin of the service. + * + * @param username the username to target for agreement revocation. + * @return an empty 204 no content response on successful update. + * @throws FinalForbiddenException if the username doesn't match the logged in user, and the logged in user isn't an admin. + * @throws NotFoundException if the user in question doesn't have an active agreement to revoke. + * @throws ServerErrorException if the record cannot be properly revoked. + */ @DELETE @Path("{efUsername}") public Response revokeAgreementForUser(@PathParam("efUsername") String username) { @@ -119,18 +150,19 @@ public class PublisherAgreementResource { // The owner or admin can retrieve a specific agreement checkIfAdminOrSelf(username); + // check that an agreement exists before revoking Optional<PeopleDocumentData> fetchResult = foundationService.fetchMostRecentDocument(username); if (fetchResult.isEmpty()) { throw new NotFoundException(String.format(NOT_FOUND_MSG_FORMAT, username)); } - String currentUser = userProfile.getCurrentUser().name(); - - Optional<PeopleDocumentData> updateResult = agreementService.revokePublisherAgreement(fetchResult.get(), currentUser); + // revokes the agreement for the targeted account + Optional<PeopleDocumentData> updateResult = agreementService.revokePublisherAgreement(fetchResult.get(), username); if (updateResult.isEmpty()) { throw new ServerErrorException("Internal Server Error", Status.INTERNAL_SERVER_ERROR); } + // return a 204 response on success return Response.noContent().build(); } @@ -150,7 +182,7 @@ public class PublisherAgreementResource { } // Ensure GH handle from current user same as in request body. - if (!StringUtils.equalsIgnoreCase(userProfile.getCurrentUser().githubHandle(), request.githubHandle())) { + if (!StringUtils.equalsIgnoreCase(getLoggedInUser().githubHandle(), request.githubHandle())) { throw new BadRequestException("The github_handle does not match our records."); } } @@ -162,12 +194,25 @@ public class PublisherAgreementResource { * @param urlUsername The username in the request URL * @return True if current user can access endpoint */ - void checkIfAdminOrSelf(String urlUsername) { + private void checkIfAdminOrSelf(String urlUsername) { // Reject request if current user is not in URL and they aren't an admin - EfUser user = userProfile.getCurrentUser(); + EfUser user = getLoggedInUser(); if (!urlUsername.equalsIgnoreCase(user.name()) && config.adminUsers().stream().noneMatch(email -> email.equalsIgnoreCase(user.mail()))) { throw new FinalForbiddenException(String.format("Access denied to resources for: %s", urlUsername)); } } -} \ No newline at end of file + + /** + * Using the logged in users username, fetch the associated EF profile. + * + * @return the logged in user account + * @throws ApplicationException if no user account can be mapped to the user account. This is typically when there is something wrong + * with the service. + */ + private EfUser getLoggedInUser() { + return profile + .fetchUserByUsername(ident.getPrincipal().getName(), true) + .orElseThrow(() -> new NotFoundException("No user profile found for logged in user")); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 04dc27f..d78ed36 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,16 +1,13 @@ quarkus.http.root-path=/openvsx quarkus.log.level=INFO -quarkus.oidc.enabled=false - fdndb-api/mp-rest/url=http://foundationdb:8095 %dev.fdndb-api/mp-rest/url=http://localhost:10112 ## Bypass the cache for user objects, as we want to always have latest eclipse.cache.cache-bypass-classes=org.eclipsefoundation.efservices.api.models.EfUser -eclipse.security.oauth2.filter.enabled=true -eclipse.security.oauth2.filter.always-on.enabled=true +eclipse.security.oauth2.filter.enabled=false quarkus.log.file.enable=false -quarkus.micrometer.enabled=true \ No newline at end of file +quarkus.micrometer.enabled=true diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java index 6124c93..6f13fd6 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java +++ b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java @@ -15,64 +15,58 @@ import java.util.Map; import java.util.Optional; import org.eclipsefoundation.openvsx.test.helpers.SchemaNamespaceHelper; +import org.eclipsefoundation.testing.helpers.AuthHelper; import org.eclipsefoundation.testing.helpers.TestCaseHelper; import org.eclipsefoundation.testing.models.EndpointTestBuilder; import org.eclipsefoundation.testing.models.EndpointTestCase; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.security.TestSecurity; @QuarkusTest class ProfileResourceTest { public static final String BASE_URL = "profile"; - - public static final Optional<Map<String, Object>> userCreds = Optional.of(Map.of("Authorization", "Bearer token2")); - - public static final Optional<Map<String, Object>> noUserCreds = Optional.of(Map.of("Authorization", "Bearer token6")); - - public static final Optional<Map<String, Object>> invalidCreds = Optional.of(Map.of("Authorization", "Bearer token1")); + public static final String FAKEUSER_PROFILE = "fakeuser"; public static final EndpointTestCase GET_CURRENT_SUCCESS = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.EF_USERS_SCHEMA_PATH) - .setHeaderParams(userCreds) .build(); /* * GET CURRENT USER */ @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGetProfile_success() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGetProfile_success_validateResponseFormat() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckFormat().run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGetProfile_success_validateSchema() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckSchema().run(); } @Test + @TestSecurity(user = "") void testGetProfile_failure_anonymousToken() { - EndpointTestBuilder - .from(TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, null) - .setStatusCode(403) - .setHeaderParams(noUserCreds) - .build()) - .run(); + EndpointTestBuilder.from(TestCaseHelper.prepareTestCase(BASE_URL, new String[] {}, null).setStatusCode(401).build()).run(); } @Test - void testGetProfile_failure_invalidCreds() { + void testGetProfile_failure_noValidToken() { EndpointTestBuilder .from(TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, null) - .setStatusCode(403) - .setHeaderParams(invalidCreds) + .setStatusCode(401) + .setHeaderParams(Optional.of(Map.of("Authorization", "Bearer token1"))) .build()) .run(); } diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java index 3bac452..5f8d34f 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java +++ b/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java @@ -16,6 +16,7 @@ import java.util.Optional; import org.eclipsefoundation.openvsx.models.AgreementSigningRequest; import org.eclipsefoundation.openvsx.test.helpers.SchemaNamespaceHelper; +import org.eclipsefoundation.testing.helpers.AuthHelper; import org.eclipsefoundation.testing.helpers.TestCaseHelper; import org.eclipsefoundation.testing.models.EndpointTestBuilder; import org.eclipsefoundation.testing.models.EndpointTestCase; @@ -25,6 +26,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.security.TestSecurity; import io.restassured.http.ContentType; import jakarta.inject.Inject; @@ -33,77 +35,66 @@ class PublisherAgreementResourceTest { public static final String BASE_URL = "publisher_agreement"; public static final String USER_URL = BASE_URL + "/{efusername}"; - public static final Optional<Map<String, Object>> userCreds = Optional.of(Map.of("Authorization", "Bearer token2")); - - public static final Optional<Map<String, Object>> userNoDocCreds = Optional.of(Map.of("Authorization", "Bearer token5")); + public static final String FAKEUSER_PROFILE = "fakeuser"; + public static final String OTHERUSER_PROFILE = "otheruser"; + public static final String NODOC_PROFILE = "nodoc"; public static final Optional<Map<String, Object>> invalidCreds = Optional.of(Map.of("Authorization", "Bearer token1")); - public static final Optional<Map<String, Object>> docCreateCreds = Optional.of(Map.of("Authorization", "Bearer token7")); - public static final EndpointTestCase GET_CURRENT_SUCCESS = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setHeaderParams(userCreds) .build(); public static final EndpointTestCase GET_CURRENT_NOT_FOUND = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) .setStatusCode(404) - .setHeaderParams(userNoDocCreds) .build(); public static final EndpointTestCase BAD_CREDS = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, null) - .setStatusCode(403) + .setStatusCode(401) .setHeaderParams(invalidCreds) .build(); public static final EndpointTestCase POST_CURRENT_CONFLICT = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, null) .setStatusCode(409) - .setHeaderParams(userCreds) .build(); public static final EndpointTestCase POST_CURRENT_INVALID_HANDLE = TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) .setStatusCode(400) - .setHeaderParams(userCreds) .build(); public static final EndpointTestCase GET_USER_SUCCESS = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setHeaderParams(userCreds) .build(); public static final EndpointTestCase GET_USER_NOT_FOUND = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) .setStatusCode(404) - .setHeaderParams(userNoDocCreds) .build(); public static final EndpointTestCase FOR_USER_BAD_CREDS = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setStatusCode(403) + .setStatusCode(401) .setHeaderParams(invalidCreds) .build(); public static final EndpointTestCase REVOKE_SUCCESS = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setHeaderParams(userCreds) .setStatusCode(204) .build(); public static final EndpointTestCase REVOKE_NO_DOC = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) .setStatusCode(404) - .setHeaderParams(userNoDocCreds) .build(); public static final EndpointTestCase REVOKE_INVALID_USER = TestCaseHelper .prepareTestCase(USER_URL, new String[] { "other" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) .setStatusCode(403) .setResponseContentType(ContentType.JSON) - .setHeaderParams(userCreds) .build(); @Inject @@ -113,31 +104,37 @@ class PublisherAgreementResourceTest { * GET CURRENT USER */ @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_success() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_success_validateResponseFormat() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckFormat().run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_success_validateSchema() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckSchema().run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_failure_notFound() { EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_failure_notFound_validateResponseFormat() { EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckFormat().run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_currentUser_failure_notFound_validateSchema() { EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckSchema().run(); } @@ -151,37 +148,42 @@ class PublisherAgreementResourceTest { * POST CURRENT USER */ @Test + @TestSecurity(user = NODOC_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_success() { EndpointTestBuilder .from(TestCaseHelper .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setHeaderParams(docCreateCreds) .build()) .doPost(generateSigningSample("nodoc")) .run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_conflict() { EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_conflict_validateResponseFormat() { EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_conflict_validateSchema() { EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_failure_invalidHandle() { EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testPost_currentUser_failure_invalidHandle_validateFormat() { EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).andCheckFormat().run(); } @@ -195,31 +197,37 @@ class PublisherAgreementResourceTest { * GET FOR USER */ @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_getForUser_success() { EndpointTestBuilder.from(GET_USER_SUCCESS).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_geFortUser_success_validateResponseFormat() { EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckFormat().run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_getForUser_success_validateSchema() { EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckSchema().run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_getForUser_failure_notFound() { EndpointTestBuilder.from(GET_USER_NOT_FOUND).run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_getForUser_failure_notFound_validateResponseFormat() { EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckFormat().run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testGet_getForUser_failure_notFound_validateSchema() { EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckSchema().run(); } @@ -233,21 +241,25 @@ class PublisherAgreementResourceTest { * DELETE FOR USER */ @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testDelete_deleteForUser_success() { EndpointTestBuilder.from(REVOKE_SUCCESS).doDelete(null).run(); } @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testDelete_deleteForUser_failure_invalidUser() { EndpointTestBuilder.from(REVOKE_INVALID_USER).doDelete(null).run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testDelete_deleteForUser_failure_noDoc() { EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).run(); } @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) void testDelete_deleteForUser_failure_noDoc_validateSchema() { EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).andCheckSchema().run(); } diff --git a/src/test/java/org/eclipsefoundation/openvsx/test/api/MockDrupalOAuthAPI.java b/src/test/java/org/eclipsefoundation/openvsx/test/api/MockDrupalOAuthAPI.java deleted file mode 100644 index e845c5d..0000000 --- a/src/test/java/org/eclipsefoundation/openvsx/test/api/MockDrupalOAuthAPI.java +++ /dev/null @@ -1,116 +0,0 @@ -/********************************************************************* -* Copyright (c) 2023 Eclipse Foundation. -* -* This program and the accompanying materials are made -* available under the terms of the Eclipse Public License 2.0 -* which is available at https://www.eclipse.org/legal/epl-2.0/ -* -* Author: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org> -* -* SPDX-License-Identifier: EPL-2.0 -**********************************************************************/ -package org.eclipsefoundation.openvsx.test.api; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.eclipsefoundation.efservices.api.DrupalOAuthAPI; -import org.eclipsefoundation.efservices.api.models.DrupalOAuthData; -import org.eclipsefoundation.efservices.api.models.DrupalOAuthDataBuilder; -import org.eclipsefoundation.efservices.api.models.DrupalUserInfo; -import org.eclipsefoundation.efservices.helpers.DrupalAuthHelper; -import org.eclipsefoundation.utils.exception.FinalForbiddenException; - -import io.quarkus.test.Mock; -import jakarta.enterprise.context.ApplicationScoped; - -@Mock -@RestClient -@ApplicationScoped -public class MockDrupalOAuthAPI implements DrupalOAuthAPI { - - List<DrupalOAuthData> tokens; - List<DrupalUserInfo> users; - - public MockDrupalOAuthAPI() { - tokens = new ArrayList<>(); - tokens - .addAll(Arrays - .asList(DrupalOAuthDataBuilder - .builder() - .accessToken("token1") - .clientId("client-id") - .expires(1674111182) - .scope("read write") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token2") - .clientId("test-id") - .userId("42") - .expires(Instant.now().getEpochSecond() + 20000) - .scope("read write admin") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token3") - .clientId("test-id") - .expires(1234567890) - .scope("read admin") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token4") - .clientId("client-id") - .expires(Instant.now().getEpochSecond() + 20000) - .scope("read write") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token5") - .clientId("test-id") - .userId("333") - .expires(Instant.now().getEpochSecond() + 20000) - .scope("read write admin") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token6") - .clientId("test-id") - .expires(Instant.now().getEpochSecond() + 20000) - .scope("read write admin") - .build(), - DrupalOAuthDataBuilder - .builder() - .accessToken("token7") - .clientId("test-id") - .userId("444") - .expires(Instant.now().getEpochSecond() + 20000) - .scope("read write admin") - .build())); - - users = new ArrayList<>(); - users - .addAll(Arrays - .asList(new DrupalUserInfo("42", "fakeuser", "fakeuser"), new DrupalUserInfo("333", "otheruser", "other"), - new DrupalUserInfo("444", "nodoc", "nodoc"))); - } - - @Override - public DrupalOAuthData getTokenInfo(String token) { - return tokens.stream().filter(t -> t.accessToken().equalsIgnoreCase(token)).findFirst().orElse(null); - } - - @Override - public DrupalUserInfo getUserInfoFromToken(String token) { - DrupalOAuthData tokenInfo = getTokenInfo(DrupalAuthHelper.stripBearerToken(token)); - if (tokenInfo == null || tokenInfo.userId() == null) { - throw new FinalForbiddenException("The access token " + DrupalAuthHelper.stripBearerToken(token) + " provided is invalid"); - } - - return users.stream().filter(u -> u.sub().equalsIgnoreCase(tokenInfo.userId())).findFirst().orElse(null); - } -} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 9397921..6389235 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -1,20 +1,15 @@ ## OIDC Connection/Authentication Info -quarkus.oauth2.enabled=false quarkus.oidc.enabled=false quarkus.keycloak.devservices.enabled=false quarkus.oidc-client.enabled=false +eclipse.security.oauth2.filter.enabled=false -eclipse.security.oauth2.filter.valid-client-ids=test-id -eclipse.security.oauth2.filter.valid-scopes=read,write,admin -eclipse.security.oauth2.filter.enabled=true -eclipse.security.oauth2.filter.always-on.enabled=true +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus/ +quarkus.oidc.client-id=quarkus-service-app +quarkus.oidc.application-type=service eclipse.openvsx.publisher-agreement.doc-id=sampleId eclipse.openvsx.publisher-agreement.doc-version=1 eclipse.openvsx.publisher-agreement.admin-users=admin@email.com -eclipse.security.oauth2.token-generation.client-secret=sample -eclipse.security.oauth2.token-generation.client-id=sample -eclipse.security.oauth2.token-generation.scope=sample - quarkus.jacoco.includes=**/openvsx/**/* -- GitLab From 0c96c2cf235549f427c5793d3f68490fa0b3567b Mon Sep 17 00:00:00 2001 From: Martin Lowe <martin.lowe@eclipse-foundation.org> Date: Wed, 4 Jun 2025 11:15:41 -0400 Subject: [PATCH 2/2] fix: Add required role to the endpoint calls --- .../openvsx/namespace/OpenVSXParameters.java | 19 + .../openvsx/resources/ProfileResource.java | 5 +- .../resources/PublisherAgreementResource.java | 5 +- .../resources/ProfileResourceTest.java | 20 +- .../PublisherAgreementResourceTest.java | 509 +++++++++--------- 5 files changed, 306 insertions(+), 252 deletions(-) create mode 100644 src/main/java/org/eclipsefoundation/openvsx/namespace/OpenVSXParameters.java diff --git a/src/main/java/org/eclipsefoundation/openvsx/namespace/OpenVSXParameters.java b/src/main/java/org/eclipsefoundation/openvsx/namespace/OpenVSXParameters.java new file mode 100644 index 0000000..5909edf --- /dev/null +++ b/src/main/java/org/eclipsefoundation/openvsx/namespace/OpenVSXParameters.java @@ -0,0 +1,19 @@ +/********************************************************************* +* Copyright (c) 2025 Eclipse Foundation. +* +* This program and the accompanying materials are made +* available under the terms of the Eclipse Public License 2.0 +* which is available at https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +**********************************************************************/ +package org.eclipsefoundation.openvsx.namespace; + +/** + * Shared parameters used in the operation of the API. + */ +public class OpenVSXParameters { + public static final String DEFAULT_ACCESS_ROLE = "openvsx_publisher_agreement"; + + private OpenVSXParameters() {} +} diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java index 659af96..cf53436 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java @@ -15,15 +15,16 @@ import java.util.Arrays; import org.eclipsefoundation.efservices.services.ProfileService; import org.eclipsefoundation.http.exception.ApplicationException; +import org.eclipsefoundation.openvsx.namespace.OpenVSXParameters; -import io.quarkus.security.Authenticated; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; -@Authenticated @Path("profile") +@RolesAllowed(OpenVSXParameters.DEFAULT_ACCESS_ROLE) public class ProfileResource { private final ProfileService profile; diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java index 1c93e27..8fb3582 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResource.java @@ -21,12 +21,13 @@ import org.eclipsefoundation.http.exception.ApplicationException; import org.eclipsefoundation.openvsx.config.PublisherAgreementConfig; import org.eclipsefoundation.openvsx.models.AgreementSigningRequest; import org.eclipsefoundation.openvsx.models.PublisherAgreementData; +import org.eclipsefoundation.openvsx.namespace.OpenVSXParameters; import org.eclipsefoundation.openvsx.services.FoundationOperationService; import org.eclipsefoundation.openvsx.services.PublisherAgreementService; import org.eclipsefoundation.utils.exception.FinalForbiddenException; -import io.quarkus.security.Authenticated; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -41,8 +42,8 @@ import jakarta.ws.rs.core.Response.Status; /** * Resource containing calls for retrieving and signing OpenVSX publisher agreements. */ -@Authenticated @Path("publisher_agreement") +@RolesAllowed(OpenVSXParameters.DEFAULT_ACCESS_ROLE) public class PublisherAgreementResource { private static final String NOT_FOUND_MSG_FORMAT = "Unable to find agreement for user: %s"; diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java index 6f13fd6..58fe3a1 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java +++ b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java @@ -14,8 +14,8 @@ package org.eclipsefoundation.openvsx.resources; import java.util.Map; import java.util.Optional; +import org.eclipsefoundation.openvsx.namespace.OpenVSXParameters; import org.eclipsefoundation.openvsx.test.helpers.SchemaNamespaceHelper; -import org.eclipsefoundation.testing.helpers.AuthHelper; import org.eclipsefoundation.testing.helpers.TestCaseHelper; import org.eclipsefoundation.testing.models.EndpointTestBuilder; import org.eclipsefoundation.testing.models.EndpointTestCase; @@ -37,19 +37,19 @@ class ProfileResourceTest { * GET CURRENT USER */ @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) void testGetProfile_success() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).run(); } @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) void testGetProfile_success_validateResponseFormat() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckFormat().run(); } @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) void testGetProfile_success_validateSchema() { EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckSchema().run(); } @@ -70,4 +70,16 @@ class ProfileResourceTest { .build()) .run(); } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = "user") + void testGetProfile_failure_noValidRole() { + EndpointTestBuilder + .from(TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, null) + .setStatusCode(403) + .setHeaderParams(Optional.of(Map.of("Authorization", "Bearer token1"))) + .build()) + .run(); + } } diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java index 5f8d34f..50259db 100644 --- a/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java +++ b/src/test/java/org/eclipsefoundation/openvsx/resources/PublisherAgreementResourceTest.java @@ -15,8 +15,8 @@ import java.util.Map; import java.util.Optional; import org.eclipsefoundation.openvsx.models.AgreementSigningRequest; +import org.eclipsefoundation.openvsx.namespace.OpenVSXParameters; import org.eclipsefoundation.openvsx.test.helpers.SchemaNamespaceHelper; -import org.eclipsefoundation.testing.helpers.AuthHelper; import org.eclipsefoundation.testing.helpers.TestCaseHelper; import org.eclipsefoundation.testing.models.EndpointTestBuilder; import org.eclipsefoundation.testing.models.EndpointTestCase; @@ -32,248 +32,269 @@ import jakarta.inject.Inject; @QuarkusTest class PublisherAgreementResourceTest { - public static final String BASE_URL = "publisher_agreement"; - public static final String USER_URL = BASE_URL + "/{efusername}"; - - public static final String FAKEUSER_PROFILE = "fakeuser"; - public static final String OTHERUSER_PROFILE = "otheruser"; - public static final String NODOC_PROFILE = "nodoc"; - - public static final Optional<Map<String, Object>> invalidCreds = Optional.of(Map.of("Authorization", "Bearer token1")); - - public static final EndpointTestCase GET_CURRENT_SUCCESS = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .build(); - - public static final EndpointTestCase GET_CURRENT_NOT_FOUND = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) - .setStatusCode(404) - .build(); - - public static final EndpointTestCase BAD_CREDS = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, null) - .setStatusCode(401) - .setHeaderParams(invalidCreds) - .build(); - - public static final EndpointTestCase POST_CURRENT_CONFLICT = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, null) - .setStatusCode(409) - .build(); - - public static final EndpointTestCase POST_CURRENT_INVALID_HANDLE = TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) - .setStatusCode(400) - .build(); - - public static final EndpointTestCase GET_USER_SUCCESS = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .build(); - - public static final EndpointTestCase GET_USER_NOT_FOUND = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) - .setStatusCode(404) - .build(); - - public static final EndpointTestCase FOR_USER_BAD_CREDS = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setStatusCode(401) - .setHeaderParams(invalidCreds) - .build(); - - public static final EndpointTestCase REVOKE_SUCCESS = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .setStatusCode(204) - .build(); - - public static final EndpointTestCase REVOKE_NO_DOC = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) - .setStatusCode(404) - .build(); - - public static final EndpointTestCase REVOKE_INVALID_USER = TestCaseHelper - .prepareTestCase(USER_URL, new String[] { "other" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) - .setStatusCode(403) - .setResponseContentType(ContentType.JSON) - .build(); - - @Inject - ObjectMapper mapper; - - /* - * GET CURRENT USER - */ - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_success() { - EndpointTestBuilder.from(GET_CURRENT_SUCCESS).run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_success_validateResponseFormat() { - EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_success_validateSchema() { - EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckSchema().run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_failure_notFound() { - EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_failure_notFound_validateResponseFormat() { - EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_currentUser_failure_notFound_validateSchema() { - EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckSchema().run(); - } - - @Test - void testGet_currentUser_failure_badCreds() { - EndpointTestBuilder.from(BAD_CREDS).run(); - } - - /* - * POST CURRENT USER - */ - @Test - @TestSecurity(user = NODOC_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_success() { - EndpointTestBuilder - .from(TestCaseHelper - .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) - .build()) - .doPost(generateSigningSample("nodoc")) - .run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_conflict() { - EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_conflict_validateResponseFormat() { - EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_conflict_validateSchema() { - EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_failure_invalidHandle() { - EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testPost_currentUser_failure_invalidHandle_validateFormat() { - EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).andCheckFormat().run(); - } - - @Test - void testPost_currentUser_failure_badCreds() { - EndpointTestBuilder.from(BAD_CREDS).doPost(generateSigningSample("fakeuser")).run(); - } - - /* - * GET FOR USER - */ - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_getForUser_success() { - EndpointTestBuilder.from(GET_USER_SUCCESS).run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_geFortUser_success_validateResponseFormat() { - EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_getForUser_success_validateSchema() { - EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckSchema().run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_getForUser_failure_notFound() { - EndpointTestBuilder.from(GET_USER_NOT_FOUND).run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_getForUser_failure_notFound_validateResponseFormat() { - EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckFormat().run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testGet_getForUser_failure_notFound_validateSchema() { - EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckSchema().run(); - } - - @Test - void testGet_getForUser_failure_badCreds() { - EndpointTestBuilder.from(FOR_USER_BAD_CREDS).run(); - } - - /* - * DELETE FOR USER - */ - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testDelete_deleteForUser_success() { - EndpointTestBuilder.from(REVOKE_SUCCESS).doDelete(null).run(); - } - - @Test - @TestSecurity(user = FAKEUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testDelete_deleteForUser_failure_invalidUser() { - EndpointTestBuilder.from(REVOKE_INVALID_USER).doDelete(null).run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testDelete_deleteForUser_failure_noDoc() { - EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).run(); - } - - @Test - @TestSecurity(user = OTHERUSER_PROFILE, roles = AuthHelper.DEFAULT_ROLE) - void testDelete_deleteForUser_failure_noDoc_validateSchema() { - EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).andCheckSchema().run(); - } - - @Test - void testDelete_deleteForUser_failure_badCreds() { - EndpointTestBuilder.from(FOR_USER_BAD_CREDS).run(); - } - - private String generateSigningSample(String ghHandle) { - try { - return mapper.writeValueAsString(new AgreementSigningRequest("1", ghHandle)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + public static final String BASE_URL = "publisher_agreement"; + public static final String USER_URL = BASE_URL + "/{efusername}"; + + public static final String FAKEUSER_PROFILE = "fakeuser"; + public static final String OTHERUSER_PROFILE = "otheruser"; + public static final String NODOC_PROFILE = "nodoc"; + + public static final Optional<Map<String, Object>> invalidCreds = Optional.of(Map.of("Authorization", "Bearer token1")); + + public static final EndpointTestCase GET_CURRENT_SUCCESS = TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) + .build(); + + public static final EndpointTestCase GET_CURRENT_NOT_FOUND = TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(404) + .build(); + + public static final EndpointTestCase BAD_CREDS = TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, null) + .setStatusCode(401) + .setHeaderParams(invalidCreds) + .build(); + + public static final EndpointTestCase POST_CURRENT_CONFLICT = TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, null) + .setStatusCode(409) + .build(); + + public static final EndpointTestCase POST_CURRENT_INVALID_HANDLE = TestCaseHelper + .prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(400) + .build(); + + public static final EndpointTestCase GET_USER_SUCCESS = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) + .build(); + + public static final EndpointTestCase GET_USER_NOT_FOUND = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(404) + .build(); + + public static final EndpointTestCase FOR_USER_BAD_CREDS = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) + .setStatusCode(401) + .setHeaderParams(invalidCreds) + .build(); + + public static final EndpointTestCase FOR_USER_BAD_ROLE = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) + .setStatusCode(403) + .setHeaderParams(invalidCreds) + .build(); + + public static final EndpointTestCase REVOKE_SUCCESS = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH) + .setStatusCode(204) + .build(); + + public static final EndpointTestCase REVOKE_NO_DOC = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "name" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(404) + .build(); + + public static final EndpointTestCase REVOKE_INVALID_USER = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "other" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(403) + .setResponseContentType(ContentType.JSON) + .build(); + public static final EndpointTestCase REVOKE_INVALID_ROLE = TestCaseHelper + .prepareTestCase(USER_URL, new String[] { "fakeuser" }, SchemaNamespaceHelper.ERROR_SCHEMA_PATH) + .setStatusCode(403) + .setResponseContentType(ContentType.JSON) + .build(); + + @Inject + ObjectMapper mapper; + + /* + * GET CURRENT USER + */ + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_success() { + EndpointTestBuilder.from(GET_CURRENT_SUCCESS).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_success_validateResponseFormat() { + EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_success_validateSchema() { + EndpointTestBuilder.from(GET_CURRENT_SUCCESS).andCheckSchema().run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_failure_notFound() { + EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_failure_notFound_validateResponseFormat() { + EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_currentUser_failure_notFound_validateSchema() { + EndpointTestBuilder.from(GET_CURRENT_NOT_FOUND).andCheckSchema().run(); + } + + @Test + void testGet_currentUser_failure_badCreds() { + EndpointTestBuilder.from(BAD_CREDS).run(); + } + + /* + * POST CURRENT USER + */ + @Test + @TestSecurity(user = NODOC_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_success() { + EndpointTestBuilder + .from(TestCaseHelper.prepareTestCase(BASE_URL, new String[] {}, SchemaNamespaceHelper.PUBLISHER_AGREEMENT_SCHEMA_PATH).build()) + .doPost(generateSigningSample("nodoc")) + .run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_conflict() { + EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_conflict_validateResponseFormat() { + EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_conflict_validateSchema() { + EndpointTestBuilder.from(POST_CURRENT_CONFLICT).doPost(generateSigningSample("fakeuser")).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_failure_invalidHandle() { + EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testPost_currentUser_failure_invalidHandle_validateFormat() { + EndpointTestBuilder.from(POST_CURRENT_INVALID_HANDLE).doPost(generateSigningSample("otheruser")).andCheckFormat().run(); + } + + @Test + void testPost_currentUser_failure_badCreds() { + EndpointTestBuilder.from(BAD_CREDS).doPost(generateSigningSample("fakeuser")).run(); + } + + /* + * GET FOR USER + */ + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_getForUser_success() { + EndpointTestBuilder.from(GET_USER_SUCCESS).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_geFortUser_success_validateResponseFormat() { + EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_getForUser_success_validateSchema() { + EndpointTestBuilder.from(GET_USER_SUCCESS).andCheckSchema().run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_getForUser_failure_notFound() { + EndpointTestBuilder.from(GET_USER_NOT_FOUND).run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_getForUser_failure_notFound_validateResponseFormat() { + EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckFormat().run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testGet_getForUser_failure_notFound_validateSchema() { + EndpointTestBuilder.from(GET_USER_NOT_FOUND).andCheckSchema().run(); + } + + @Test + void testGet_getForUser_failure_badCreds() { + EndpointTestBuilder.from(FOR_USER_BAD_CREDS).run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = "profile") + void testGet_getForUser_failure_noValidRole() { + EndpointTestBuilder.from(FOR_USER_BAD_ROLE).run(); + } + + /* + * DELETE FOR USER + */ + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testDelete_deleteForUser_success() { + EndpointTestBuilder.from(REVOKE_SUCCESS).doDelete(null).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testDelete_deleteForUser_failure_invalidUser() { + EndpointTestBuilder.from(REVOKE_INVALID_USER).doDelete(null).run(); + } + + @Test + @TestSecurity(user = FAKEUSER_PROFILE, roles = "role") + void testDelete_deleteForUser_failure_invalidRole() { + EndpointTestBuilder.from(REVOKE_INVALID_ROLE).doDelete(null).run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testDelete_deleteForUser_failure_noDoc() { + EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).run(); + } + + @Test + @TestSecurity(user = OTHERUSER_PROFILE, roles = OpenVSXParameters.DEFAULT_ACCESS_ROLE) + void testDelete_deleteForUser_failure_noDoc_validateSchema() { + EndpointTestBuilder.from(REVOKE_NO_DOC).doDelete(null).andCheckSchema().run(); + } + + @Test + void testDelete_deleteForUser_failure_badCreds() { + EndpointTestBuilder.from(FOR_USER_BAD_CREDS).run(); + } + + private String generateSigningSample(String ghHandle) { + try { + return mapper.writeValueAsString(new AgreementSigningRequest("1", ghHandle)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } + } } -- GitLab