diff --git a/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtils.java b/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtils.java index 82eb494c2d..5c1e7422fc 100644 --- a/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtils.java +++ b/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtils.java @@ -13,82 +13,6 @@ import org.springframework.core.annotation.AnnotationUtils; public class AuthTestUtils { - - public static void assertHasAnnotation( - Object service, String methodName, Class annotationClass) { - List targetMethods = - Arrays.stream(service.getClass().getDeclaredMethods()) - .filter(method -> method.getName().equals(methodName)) - .toList(); - assertThat(targetMethods.size(), greaterThan(0)); - targetMethods - .stream() // we need to check all methods with the same name in case they are overloaded - .forEach( - targetMethod -> - assertThat( - AnnotationUtils.findAnnotation(targetMethod, annotationClass), notNullValue())); - } - - public static void assertHasPermissionEnforced( - Object service, - String methodName, - Class annotationClass, - String permissionName) { - List targetMethods = - Arrays.stream(service.getClass().getDeclaredMethods()) - .filter(method -> method.getName().equals(methodName)) - .toList(); - assertThat(targetMethods.size(), greaterThan(0)); - targetMethods - .stream() // we need to check all methods with the same name in case they are overloaded - .forEach( - targetMethod -> { - Annotation annotation = AnnotationUtils.findAnnotation(targetMethod, annotationClass); - try { - String permission = - (String) MethodUtils.invokeMethod(annotation, "permission", null); - assertThat(permission, equalTo(permissionName)); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - - /** methods we don't validate annotations on -- base Object methods and methods added by Spring */ - private static final List excludedMethodNames = - List.of( - "equals", - "toString", - "hashCode", - "indexOf", - "newInstance", - "isFrozen", - "addAdvisor", - "setCallback", - "getTargetClass", - "CGLIB$findMethodProxy", - "getCallback", - "getCallbacks", - "CGLIB$SET_THREAD_CALLBACKS", - "CGLIB$SET_STATIC_CALLBACKS", - "setCallbacks", - "getTargetSource", - "getProxiedInterfaces", - "isInterfaceProxied", - "getAdvisorCount", - "getAdvisors", - "isProxyTargetClass", - "setTargetSource", - "setExposeProxy", - "isExposeProxy", - "setPreFiltered", - "isPreFiltered", - "removeAdvisor", - "replaceAdvisor", - "removeAdvice", - "toProxyConfigString", - "addAdvice"); - /** confirms all public methods of the given class are annotated according to the specs */ public static void assertAllMethodsAnnotated( Object service, Map annotationSpecs) { @@ -147,4 +71,39 @@ public static void validateMethodToSpec(Method method, AuthAnnotationSpec annota notNullValue()); } } + + /** methods we don't validate annotations on -- base Object methods and methods added by Spring */ + private static final List excludedMethodNames = + List.of( + "equals", + "toString", + "hashCode", + "indexOf", + "newInstance", + "isFrozen", + "addAdvisor", + "setCallback", + "getTargetClass", + "CGLIB$findMethodProxy", + "getCallback", + "getCallbacks", + "CGLIB$SET_THREAD_CALLBACKS", + "CGLIB$SET_STATIC_CALLBACKS", + "setCallbacks", + "getTargetSource", + "getProxiedInterfaces", + "isInterfaceProxied", + "getAdvisorCount", + "getAdvisors", + "isProxyTargetClass", + "setTargetSource", + "setExposeProxy", + "isExposeProxy", + "setPreFiltered", + "isPreFiltered", + "removeAdvisor", + "replaceAdvisor", + "removeAdvice", + "toProxyConfigString", + "addAdvice"); } diff --git a/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtilsTest.java b/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtilsTest.java index c3c1e454ad..95199e16ac 100644 --- a/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtilsTest.java +++ b/api-admin/src/test/java/bio/terra/pearl/api/admin/AuthTestUtilsTest.java @@ -1,2 +1,81 @@ -package bio.terra.pearl.api.admin;public class AuthTestUtilsTest { +package bio.terra.pearl.api.admin; + +import bio.terra.pearl.api.admin.service.auth.EnforcePortalPermission; +import bio.terra.pearl.api.admin.service.auth.SandboxOnly; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AuthTestUtilsTest { + @Test + public void testChecksAnnotatedMethod() { + AuthTestUtils.assertAllMethodsAnnotated( + new MethodAnnotatedClass(), + Map.of("doSomethingSecure", AuthAnnotationSpec.withPortalPerm("BASE"))); + + // fails if the wrong permission is specced + Assertions.assertThrows( + AssertionError.class, + () -> { + AuthTestUtils.assertAllMethodsAnnotated( + new MethodAnnotatedClass(), + Map.of("doSomethingSecure", AuthAnnotationSpec.withPortalPerm("wrongPerm"))); + }); + + // fails if the spec also expects a SandboxOnly annotation + Assertions.assertThrows( + AssertionError.class, + () -> { + AuthTestUtils.assertAllMethodsAnnotated( + new MethodAnnotatedClass(), + Map.of( + "doSomethingSecure", + AuthAnnotationSpec.withPortalPerm("wrongPerm", List.of(SandboxOnly.class)))); + }); + } + + @Test + public void testFailsUnspecifiedMethod() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> { + AuthTestUtils.assertAllMethodsAnnotated(new MethodAnnotatedClass(), Map.of()); + }); + } + + @Test + public void testFailsMethodNotAnnotated() { + Assertions.assertThrows( + AssertionError.class, + () -> { + AuthTestUtils.assertAllMethodsAnnotated( + new MethodNotAnnotatedClass(), + Map.of("doSomethingSecure", AuthAnnotationSpec.withPortalPerm("BASE"))); + }); + } + + @Test + public void testChecksSandboxAnnotated() { + AuthTestUtils.assertAllMethodsAnnotated( + new SandboxMethodAnnotatedClass(), + Map.of( + "doSomethingSecure", + AuthAnnotationSpec.withPortalPerm("BASE", List.of(SandboxOnly.class)))); + } + + public static class MethodAnnotatedClass { + @EnforcePortalPermission(permission = "BASE") + public void doSomethingSecure() {} + } + + public static class MethodNotAnnotatedClass { + public void doSomethingSecure() {} + } + + public static class SandboxMethodAnnotatedClass { + @SandboxOnly + @EnforcePortalPermission(permission = "BASE") + public void doSomethingSecure() {} + } } diff --git a/api-admin/src/test/java/bio/terra/pearl/api/admin/service/forms/SurveyExtServiceAuthTests.java b/api-admin/src/test/java/bio/terra/pearl/api/admin/service/forms/SurveyExtServiceAuthTests.java index 59d4b009b4..b01100f46a 100644 --- a/api-admin/src/test/java/bio/terra/pearl/api/admin/service/forms/SurveyExtServiceAuthTests.java +++ b/api-admin/src/test/java/bio/terra/pearl/api/admin/service/forms/SurveyExtServiceAuthTests.java @@ -1,41 +1,36 @@ package bio.terra.pearl.api.admin.service.forms; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.when; import bio.terra.pearl.api.admin.AuthAnnotationSpec; import bio.terra.pearl.api.admin.AuthTestUtils; import bio.terra.pearl.api.admin.BaseSpringBootTest; -import bio.terra.pearl.api.admin.service.auth.AuthUtilService; -import bio.terra.pearl.api.admin.service.auth.EnforcePortalPermission; -import bio.terra.pearl.api.admin.service.auth.EnforcePortalStudyEnvPermission; import bio.terra.pearl.api.admin.service.auth.SandboxOnly; import bio.terra.pearl.api.admin.service.auth.context.PortalAuthContext; -import bio.terra.pearl.core.model.admin.AdminUser; +import bio.terra.pearl.core.factory.admin.AdminUserBundle; +import bio.terra.pearl.core.factory.admin.AdminUserFactory; +import bio.terra.pearl.core.factory.admin.PortalAdminUserFactory; +import bio.terra.pearl.core.factory.portal.PortalFactory; +import bio.terra.pearl.core.factory.survey.SurveyFactory; import bio.terra.pearl.core.model.portal.Portal; import bio.terra.pearl.core.model.survey.Survey; import bio.terra.pearl.core.service.exception.NotFoundException; -import bio.terra.pearl.core.service.study.StudyEnvironmentService; -import bio.terra.pearl.core.service.study.StudyEnvironmentSurveyService; -import bio.terra.pearl.core.service.survey.SurveyService; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.transaction.annotation.Transactional; public class SurveyExtServiceAuthTests extends BaseSpringBootTest { @Autowired private SurveyExtService surveyExtService; - - @MockBean private AuthUtilService mockAuthUtilService; - @MockBean private SurveyService mockSurveyService; - @MockBean private StudyEnvironmentSurveyService mockStudyEnvironmentSurveyService; - @MockBean private StudyEnvironmentService mockStudyEnvironmentService; + @Autowired private PortalFactory portalFactory; + @Autowired private SurveyFactory surveyFactory; + @Autowired private AdminUserFactory adminUserFactory; + @Autowired private PortalAdminUserFactory portalAdminUserFactory; @Test public void assertAllMethods() { @@ -63,37 +58,28 @@ public void assertAllMethods() { } @Test - public void getRequiresSurveyMatchedToPortal() { - AdminUser user = AdminUser.builder().superuser(false).build(); - Portal portal = Portal.builder().shortcode("testSurveyGet").id(UUID.randomUUID()).build(); - Survey matchedSurvey = configureMockSurvey("testMatchedToPortal", 1, portal.getId()); - Survey unmatchedSurvey = configureMockSurvey("testUnmatchedToPortal", 1, UUID.randomUUID()); - when(mockAuthUtilService.authUserToPortal(user, portal.getShortcode())).thenReturn(portal); - when(mockAuthUtilService.authSurveyToPortal(portal, "testMatchedToPortal", 1)) - .thenReturn(matchedSurvey); - when(mockAuthUtilService.authSurveyToPortal(portal, "testUnmatchedToPortal", 1)) - .thenThrow(new NotFoundException("not found")); - + @Transactional + public void getRequiresSurveyMatchedToPortal(TestInfo info) { + Portal portal = portalFactory.buildPersisted(getTestName(info)); + AdminUserBundle userBundle = + portalAdminUserFactory.buildPersistedWithPortals(getTestName(info), List.of(portal)); + Portal otherPortal = portalFactory.buildPersisted(getTestName(info)); + Survey survey = surveyFactory.buildPersisted(getTestName(info), portal.getId()); assertThat( surveyExtService.get( - PortalAuthContext.of(user, portal.getShortcode()), - matchedSurvey.getStableId(), - matchedSurvey.getVersion()), - notNullValue()); + PortalAuthContext.of(userBundle.user(), portal.getShortcode()), + survey.getStableId(), + survey.getVersion()), + equalTo(survey)); + + // not found if attempted to retrieve via the other portal Assertions.assertThrows( NotFoundException.class, () -> surveyExtService.get( - PortalAuthContext.of(user, portal.getShortcode()), - unmatchedSurvey.getStableId(), - unmatchedSurvey.getVersion())); - } - - private Survey configureMockSurvey(String stableId, int version, UUID portalId) { - Survey survey = Survey.builder().stableId(stableId).version(1).portalId(portalId).build(); - when(mockSurveyService.findByStableId(stableId, version, portalId)) - .thenReturn(Optional.of(survey)); - return survey; + PortalAuthContext.of(userBundle.user(), otherPortal.getShortcode()), + survey.getStableId(), + survey.getVersion())); } public record AuthTestSpec(