diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy index 802002fde..eeee3fe5c 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy @@ -1,22 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor -import edu.internet2.tier.shibboleth.admin.ui.domain.Organization -import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationDisplayName -import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationName -import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationURL import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository -import groovy.json.JsonOutput -import groovy.json.JsonSlurper import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.client.TestRestTemplate -import org.springframework.http.HttpEntity -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod -import org.springframework.http.MediaType -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import spock.lang.Specification @@ -111,44 +100,9 @@ class EntityDescriptorControllerVersionEndpointsIntegrationTests extends Specifi edv2.body.serviceProviderName == 'SP2' } - @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) - def 'SHIBUI-1414'() { - given: - def ed = new EntityDescriptor(entityID: 'testme', serviceProviderName: 'testme').with { - entityDescriptorRepository.save(it) - }.with { - it.setOrganization(new Organization().with { - it.organizationNames = [new OrganizationName(value: 'testme', XMLLang: 'en')] - it.organizationDisplayNames = [new OrganizationDisplayName(value: 'testme', XMLLang: 'en')] - it.organizationURLs = [new OrganizationURL(value: 'http://testme.org', XMLLang: 'en')] - it - }) - entityDescriptorRepository.save(it) - } - when: - def headers = new HttpHeaders().with { - it.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - it - } - - def allVersions = getAllEntityDescriptorVersions(ed.resourceId, List) - String edv1 = getEntityDescriptorForVersion(ed.resourceId, allVersions.body[0].id, String).body - def tedv2 = getEntityDescriptorForVersion(ed.resourceId, allVersions.body[1].id, EntityDescriptorRepresentation).body - - def aedv1 = new JsonSlurper().parseText(edv1).with { - it.put('version', tedv2.version) - it - }.with { - JsonOutput.toJson(it) - } - - def request = new HttpEntity(aedv1, headers) - def response = this.restTemplate.exchange("/api/EntityDescriptor/${ed.resourceId}", HttpMethod.PUT, request, String) - - then: - response.statusCodeValue != 400 - noExceptionThrown() - } +// Moved to its own test in the main testing package +// def 'SHIBUI-1414'() { +// } private getAllEntityDescriptorVersions(String resourceId, responseType) { this.restTemplate.getForEntity(resourceUriFor(ALL_VERSIONS_URI, resourceId), responseType) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java index 8436a114e..be0d39c05 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java @@ -116,11 +116,11 @@ public ResponseEntity getOneXml(@PathVariable String resourceId) throws Marsh } @GetMapping("/EntityDescriptor/{resourceId}/Versions/{versionId}") - @Transactional public ResponseEntity getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) throws EntityNotFoundException, ForbiddenException { // this "get by resource id" verifies that both the ED exists and the user has proper access, so needs to remain EntityDescriptor ed = entityDescriptorService.getEntityDescriptorByResourceId(resourceId); - return ResponseEntity.ok(versionService.findSpecificVersionOfEntityDescriptor(ed.getResourceId(), versionId)); + EntityDescriptorRepresentation result = versionService.findSpecificVersionOfEntityDescriptor(ed.getResourceId(), versionId); + return ResponseEntity.ok(result); } private ResponseEntity handleUploadingEntityDescriptorXml(byte[] rawXmlBytes, String spName) throws Exception { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index 04ac48d02..db769acc3 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -380,7 +380,7 @@ public List getAllRepresentationsBasedOnUserAcce @Override public List getAttributeReleaseListFromAttributeList(List attributeList) { if (attributeList == null) { - return new ArrayList(); + return new ArrayList<>(); } attributeList.removeIf(Objects::isNull); return ModelRepresentationConversions.getAttributeReleaseListFromAttributeList(attributeList); diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy index 0555f713b..f3b2bc34e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy @@ -1,8 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.StringTrimModule import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.GroupUpdatedEntityListener import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.UserUpdatedEntityListener @@ -19,6 +23,8 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.context.annotation.Primary +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL + @Configuration @Import([ShibUIConfiguration.class, CustomPropertiesConfiguration.class, SearchConfiguration.class]) @ComponentScan(basePackages=[ "edu.internet2.tier.shibboleth.admin.ui.service", "edu.internet2.tier.shibboleth.admin.ui.security.service", @@ -53,6 +59,16 @@ class BaseDataJpaTestConfiguration { return new ModelRepresentationConversions(customPropertiesConfiguration) } + @Bean + ObjectMapper objectMapper() { + ObjectMapper mapper = new ObjectMapper() + mapper.enable(SerializationFeature.INDENT_OUTPUT) + mapper.setSerializationInclusion(NON_NULL) + mapper.registerModule(new JavaTimeModule()) + mapper.registerModule(new StringTrimModule()) + return mapper + } + @Bean @Primary OpenSamlObjects openSamlObjects() { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy index c43ffe4f9..510e09e64 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy @@ -64,6 +64,9 @@ class EntityDescriptorControllerTests extends AbstractBaseDataJpaTest { @Autowired TestObjectGenerator generator + @Autowired + ObjectMapper mapper + @Autowired OpenSamlObjects openSamlObjects @@ -71,7 +74,6 @@ class EntityDescriptorControllerTests extends AbstractBaseDataJpaTest { JPAEntityDescriptorServiceImpl jpaEntityDescriptorService RandomGenerator randomGenerator - def mapper def mockRestTemplate = Mock(RestTemplate) def mockMvc @@ -91,7 +93,6 @@ class EntityDescriptorControllerTests extends AbstractBaseDataJpaTest { gb = groupService.createGroup(gb) randomGenerator = new RandomGenerator() - mapper = new ObjectMapper() controller = new EntityDescriptorController(versionService) controller.openSamlObjects = openSamlObjects diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorVersionControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorVersionControllerTests.groovy new file mode 100644 index 000000000..8875dbf51 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorVersionControllerTests.groovy @@ -0,0 +1,180 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.AbstractBaseDataJpaTest +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.Organization +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationDisplayName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationURL +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation +import edu.internet2.tier.shibboleth.admin.ui.envers.EnversVersionServiceSupport +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group +import edu.internet2.tier.shibboleth.admin.ui.security.model.Role +import edu.internet2.tier.shibboleth.admin.ui.security.model.User +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityService +import edu.internet2.tier.shibboleth.admin.ui.service.EnversEntityDescriptorVersionService +import edu.internet2.tier.shibboleth.admin.ui.service.EnversMetadataResolverVersionService +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService +import edu.internet2.tier.shibboleth.admin.ui.util.WithMockAdmin +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConversionUtils +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import org.hamcrest.Matchers +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.client.RestTemplate +import spock.lang.Subject + +import javax.persistence.EntityManager + +import static org.springframework.http.MediaType.APPLICATION_JSON +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@ContextConfiguration(classes=[EDCLocalConfig]) +class EntityDescriptorVersionControllerTests extends AbstractBaseDataJpaTest { + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Autowired + private TestEntityManager testEntityManager + + @Autowired + EntityService entityService + + @Autowired + JPAEntityDescriptorServiceImpl jpaEntityDescriptorService + + @Autowired + ObjectMapper mapper + + @Autowired + OpenSamlObjects openSamlObjects + + @Autowired + EntityDescriptorVersionService versionService + + def mockMvc + def mockRestTemplate = Mock(RestTemplate) + def resId + + @Subject + def controller + + @Transactional + def setup() { + openSamlObjects.init() + + Group gb = new Group() + gb.setResourceId("testingGroupBBB") + gb.setName("Group BBB") + gb.setValidationRegex("^(?:https?:\\/\\/)?(?:[^.]+\\.)?shib\\.org(\\/.*)?\$") + gb = groupService.createGroup(gb) + + controller = new EntityDescriptorController(versionService) + controller.openSamlObjects = openSamlObjects + controller.entityDescriptorService = jpaEntityDescriptorService + controller.restTemplate = mockRestTemplate + + mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + + Optional userRole = roleRepository.findByName("ROLE_USER") + User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") + user.setGroup(gb) + userService.save(user) + + EntityDescriptorConversionUtils.setOpenSamlObjects(openSamlObjects) + EntityDescriptorConversionUtils.setEntityService(entityService) + + // Because the audit is done with hibernate envers (which is done by a listener after a transaction commit), we have to jump + // through some hoops to get this to work like it should in this DataJPATest + // Use the TestEntityManager to get the versions saved to the db + EntityDescriptor ed = new EntityDescriptor(entityID: 'testme', serviceProviderName: 'testme').with { + entityDescriptorRepository.saveAndFlush(it) + } + testEntityManager.getEntityManager().getTransaction().commit() // get envers to write version + resId = ed.resourceId + ed = entityDescriptorRepository.findByResourceId(resId) + + testEntityManager.getEntityManager().getTransaction().begin() + ed.setOrganization(new Organization().with { + it.organizationNames = [new OrganizationName(value: 'testme', XMLLang: 'en')] + it.organizationDisplayNames = [new OrganizationDisplayName(value: 'testme', XMLLang: 'en')] + it.organizationURLs = [new OrganizationURL(value: 'http://testme.org', XMLLang: 'en')] + it + }) + entityDescriptorRepository.saveAndFlush(ed) + testEntityManager.getEntityManager().getTransaction().commit() // get envers to write version + } + + /** + * + * - No @Transactional on the method + * - + */ + @WithMockAdmin + @Transactional + def 'SHIBUI-1414'() { + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/" + resId + "/Versions")) + def allVersions = mapper.readValue(result.andReturn().getResponse().getContentAsString(), List.class) + + String edv1 = mockMvc.perform(get("/api/EntityDescriptor/" + resId + "/Versions/" + allVersions.get(0).id)).andReturn().getResponse().getContentAsString() + String edv2 = mockMvc.perform(get("/api/EntityDescriptor/" + resId + "/Versions/" + allVersions.get(1).id)).andReturn().getResponse().getContentAsString() + + def v2Version = new JsonSlurper().parseText(edv2).get("version") + def aedv1 = new JsonSlurper().parseText(edv1).with { + it.put('version', v2Version) + it + }.with { + JsonOutput.toJson(it) + } + testEntityManager.getEntityManager().getTransaction().begin() + def response = mockMvc.perform(put("/api/EntityDescriptor/" + resId).contentType(APPLICATION_JSON).content(aedv1)) + testEntityManager.getEntityManager().getTransaction().commit() + + then: + response.andExpect(status().isOk()) + noExceptionThrown() + } + + @TestConfiguration + private static class EDCLocalConfig { + @Bean + JPAEntityServiceImpl jpaEntityService(OpenSamlObjects openSamlObjects, AttributeUtility attributeUtility, + CustomPropertiesConfiguration customPropertiesConfiguration) { + return new JPAEntityServiceImpl(openSamlObjects, attributeUtility, customPropertiesConfiguration) + } + + @Bean + EntityDescriptorVersionService entityDescriptorVersionService(EnversVersionServiceSupport support, EntityDescriptorService entityDescriptorService) { + return new EnversEntityDescriptorVersionService(support, entityDescriptorService) + } + + @Bean + MetadataResolverVersionService metadataResolverVersionService(EnversVersionServiceSupport support) { + return new EnversMetadataResolverVersionService(support) + } + + @Bean + EnversVersionServiceSupport enversVersionServiceSupport(EntityManager entityManager) { + return new EnversVersionServiceSupport(entityManager) + } + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy index 6eaf954f7..90c70f38e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy @@ -63,16 +63,18 @@ class MetadataResolversControllerIntegrationTests extends AbstractBaseDataJpaTes @Autowired AttributeUtility attributeUtility + @Autowired + MetadataResolversController controller + @Autowired CustomPropertiesConfiguration customPropertiesConfiguration @Autowired - MetadataResolversController controller + ObjectMapper mapper @Autowired MetadataResolverRepository metadataResolverRepository - ObjectMapper mapper TestObjectGenerator generator MockMvc mockMvc @@ -81,11 +83,6 @@ class MetadataResolversControllerIntegrationTests extends AbstractBaseDataJpaTes @Transactional def setup() { generator = new TestObjectGenerator(attributeUtility, customPropertiesConfiguration) - mapper = new ObjectMapper() - mapper.enable(SerializationFeature.INDENT_OUTPUT) - mapper.setSerializationInclusion(NON_NULL) - mapper.registerModule(new JavaTimeModule()) - mapper.registerModule(new StringTrimModule()) metadataResolverRepository.deleteAll() mockMvc = MockMvcBuilders.standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter(mapper)).build() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy index 1173ce692..fa9239ec5 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy @@ -14,14 +14,12 @@ class PolymorphicFiltersJacksonHandlingTests extends AbstractBaseDataJpaTest { CustomPropertiesConfiguration customPropertiesConfiguration @Autowired - TestObjectGenerator testObjectGenerator - ObjectMapper mapper - def setup() { - mapper = new ObjectMapper() - mapper.enable(SerializationFeature.INDENT_OUTPUT) + @Autowired + TestObjectGenerator testObjectGenerator + def setup() { customPropertiesConfiguration.postConstruct() } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index 243779b27..d80fd3f61 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -36,6 +36,9 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { @Autowired EntityService entityService + @Autowired + ObjectMapper mapper + @Autowired OpenSamlObjects openSamlObjects @@ -47,7 +50,6 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { RandomGenerator generator JacksonTester jacksonTester - ObjectMapper mapper = new ObjectMapper() def setup() { JacksonTester.initFields(this, mapper)