diff --git a/pom.xml b/pom.xml index eae356a78165d83a19462e33b3fba87dc17e626c..72576528916d30024ec5a690017929eba18564cf 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ <surefire-plugin.version>3.3.1</surefire-plugin.version> <auto-value.version>1.10.4</auto-value.version> <hibernate.version>5.5.6.Final</hibernate.version> - <eclipse-api-version>1.2.1</eclipse-api-version> + <eclipse-api-version>1.2.2</eclipse-api-version> <org.mapstruct.version>1.5.5.Final</org.mapstruct.version> <fdndb-api-version>1.1.2</fdndb-api-version> <sonar.sources>src/main</sonar.sources> @@ -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> diff --git a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java index f0c754f77bc486e3c3b7a07d039f6ac1af77c70f..659af962a14990cff8f5324a852ac6b3869eef10 100644 --- a/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java +++ b/src/main/java/org/eclipsefoundation/openvsx/resources/ProfileResource.java @@ -13,22 +13,42 @@ package org.eclipsefoundation.openvsx.resources; import java.util.Arrays; -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 Response getProfileInfo() { // Returns the public profile data tied to the current user - return Response.ok(Arrays.asList(userProfile.getUserPublicProfileData())).build(); + 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 c346f501829704bf5d2b1aa75983eaa6e2e2170e..bd6c6b8a4a46c66c1f2c0f2212afd91f99258d89 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.getUserPublicProfileData().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.getUserPublicProfileData(); + 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,48 +109,59 @@ 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) { - // 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.getUserPublicProfileData().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 +181,7 @@ public class PublisherAgreementResource { } // Ensure GH handle from current user same as in request body. - if (!StringUtils.equalsIgnoreCase(userProfile.getUserPublicProfileData().githubHandle(), request.githubHandle())) { + if (!StringUtils.equalsIgnoreCase(getLoggedInUser().githubHandle(), request.githubHandle())) { throw new BadRequestException("The github_handle does not match our records."); } } @@ -162,12 +193,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.getUserPrivateProfileData(); + 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)); } } + + /** + * 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")); + } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 742eede55a8afdd768403ab42f00d9c757860687..746c1df04136d3bbb8dc21cadab141d77b690d23 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,13 +1,10 @@ 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 -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 diff --git a/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java b/src/test/java/org/eclipsefoundation/openvsx/resources/ProfileResourceTest.java index 6124c935d96146947d4259b49d3fe327cac067d3..6f13fd66578db3cd793164ee56792679268f293d 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 3bac45244a864ca9d7b7fd438008108ab0e1e60d..5f8d34f964c0ab691cbe9a7cca5b93ad8c15d796 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 e845c5da741dff7bc51d6f26ea903bcd662a860b..0000000000000000000000000000000000000000 --- 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 61b37f778a97ca8e0c0bee4fc6ee2ce60fd31e03..35a6ec8e7aa0ee903b25426c67c80eef7399adf0 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/**/* \ No newline at end of file