diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 915995d4c..d8e09ff56 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -151,7 +151,7 @@ public ResponseEntity update(@PathVariable String resourceId, @RequestBody Me MetadataResolver persistedResolver = resolverRepository.save(updatedResolver); doResolverInitialization(persistedResolver); - return ResponseEntity.ok(persistedResolver); + return ResponseEntity.ok(resolverRepository.findByResourceId(resourceId)); } //Versioning endpoints diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetup.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetup.groovy new file mode 100644 index 000000000..2fc57fe70 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetup.groovy @@ -0,0 +1,82 @@ +package edu.internet2.tier.shibboleth.admin.ui + + +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.security.repository.OwnershipRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.annotation.Transactional +import spock.lang.Shared +import spock.lang.Specification + +@DataJpaTest +@ContextConfiguration(classes = [BaseDataJpaTestSetupConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +abstract class BaseDataJpaTestSetup extends Specification { + static boolean setupRun = false + + @Autowired + GroupServiceForTesting groupService + + @Autowired + OwnershipRepository ownershipRepository + + @Autowired + RoleRepository roleRepository + + @Autowired + UserRepository userRepository + + @Autowired + UserService userService + + def setup() { + runOnce() + } + + // One time setup to ensure roles are in a known good state and that we have an admin user and group + @Transactional + runOnce() { + if (setupRun) return + + groupService.clearAllForTesting() + userRepository.deleteAll() + ownershipRepository.deleteAll() + roleRepository.deleteAll() + + def roles = [new Role().with { + name = 'ROLE_ADMIN' + it + }, new Role().with { + name = 'ROLE_USER' + it + }, new Role().with { + name = 'ROLE_ENABLE' + it + }, new Role().with { + name = 'ROLE_NONE' + it + }] + roles.each { + if (roleRepository.findByName(it.name).isEmpty()) { + roleRepository.save(it) + } + } + + if (userRepository.findByUsername("admin").isEmpty()) { + Optional adminRole = roleRepository.findByName("ROLE_ADMIN") + User adminUser = new User(username: "admin", roles: [adminRole.get()], password: "foo") + userService.save(adminUser) + } + setupRun = true + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetupConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetupConfiguration.groovy new file mode 100644 index 000000000..6316b9755 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestSetupConfiguration.groovy @@ -0,0 +1,51 @@ +package edu.internet2.tier.shibboleth.admin.ui + +import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration +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 +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import +import org.springframework.context.annotation.Primary + +@Configuration +@Import(ShibUIConfiguration.class) +@ComponentScan(basePackages=[ "edu.internet2.tier.shibboleth.admin.ui.service", "edu.internet2.tier.shibboleth.admin.ui.security.service" ]) +class BaseDataJpaTestSetupConfiguration { + @Bean + @Primary + GroupServiceForTesting groupServiceForTesting(GroupsRepository groupRepo, OwnershipRepository ownershipRepository) { + GroupServiceForTesting result = new GroupServiceForTesting(new GroupServiceImpl().with { + it.groupRepository = groupRepo + it.ownershipRepository = ownershipRepository + return it + }) + result.ensureAdminGroupExists() + return result + } + + @Bean + GroupUpdatedEntityListener groupUpdatedEntityListener(OwnershipRepository ownershipRepository) { + GroupUpdatedEntityListener listener = new GroupUpdatedEntityListener() + listener.init(ownershipRepository) + return listener + } + + @Bean + OpenSamlObjects openSamlObjects() { + return new OpenSamlObjects() + } + + @Bean + UserUpdatedEntityListener userUpdatedEntityListener(OwnershipRepository ownershipRepository, GroupsRepository groupRepo) { + UserUpdatedEntityListener listener = new UserUpdatedEntityListener() + listener.init(ownershipRepository, groupRepo) + return listener + } +} \ 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 ac453c783..623f4717a 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 @@ -3,46 +3,30 @@ package edu.internet2.tier.shibboleth.admin.ui.controller 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.BaseDataJpaTestSetup import edu.internet2.tier.shibboleth.admin.ui.configuration.* import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataQueryProtocolScheme -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.validator.MetadataResolverValidationService +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.* import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository -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.security.repository.GroupsRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository -import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting -import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl -import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository import edu.internet2.tier.shibboleth.admin.ui.service.* import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.ui.util.WithMockAdmin import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions import groovy.json.JsonSlurper import org.opensaml.saml.metadata.resolver.MetadataResolver import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Primary -import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MvcResult import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.transaction.annotation.Transactional -import spock.lang.Specification import spock.lang.Unroll import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL @@ -50,38 +34,26 @@ import static org.springframework.http.MediaType.APPLICATION_JSON import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* -@DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, MetadataResolverValidationConfiguration, EntitiesVersioningConfiguration, - SearchConfiguration, edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration, - PlaceholderResolverComponentsConfiguration, InternationalizationConfiguration, LocalConfig]) -@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) -@EntityScan("edu.internet2.tier.shibboleth.admin.ui") -@ActiveProfiles("no-auth") -class MetadataResolversControllerIntegrationTests extends Specification { +@ContextConfiguration(classes=[MetadataResolverValidationConfiguration, MetadataResolverConverterConfiguration, + SearchConfiguration, MetadataResolverConfiguration, EntitiesVersioningConfiguration, + edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration, + PlaceholderResolverComponentsConfiguration, MRCILocalConfig, CustomPropertiesConfiguration]) +@ActiveProfiles(["no-auth"]) +class MetadataResolversControllerIntegrationTests extends BaseDataJpaTestSetup { @Autowired AttributeUtility attributeUtility @Autowired CustomPropertiesConfiguration customPropertiesConfiguration - @Autowired - GroupServiceForTesting groupService - @Autowired MetadataResolversController controller @Autowired MetadataResolverRepository metadataResolverRepository - @Autowired - RoleRepository roleRepository - - @Autowired - UserService userService - ObjectMapper mapper TestObjectGenerator generator - MockMvc mockMvc static String BASE_URI = '/api/MetadataResolvers' @@ -96,11 +68,6 @@ class MetadataResolversControllerIntegrationTests extends Specification { mapper.registerModule(new StringTrimModule()) metadataResolverRepository.deleteAll() - Optional adminRole = roleRepository.findByName("ROLE_ADMIN") - User adminUser = new User(username: "admin", roles: [adminRole.get()], password: "foo") - userService.save(adminUser) - - groupService.clearAllForTesting() mockMvc = MockMvcBuilders.standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter(mapper)).build() } @@ -115,294 +82,289 @@ class MetadataResolversControllerIntegrationTests extends Specification { then: result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) - .andExpect(jsonPath("\$").isEmpty()) + .andExpect(jsonPath("\$").isEmpty()) + } + + @WithMockAdmin + def "GET one available MetadataResolver -> /api/MetadataResolvers"() { + given: 'One resolver is available in data store' + def resolver = new DynamicHttpMetadataResolver().with { + it.name = 'Test DynamicHttpMetadataResolver' + it + } + metadataResolverRepository.save(resolver) + + when: 'GET request is made' + def result = mockMvc.perform(get(BASE_URI)) + + then: + result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.[0].name").value("Test DynamicHttpMetadataResolver")) + .andExpect(jsonPath("\$.[0].['@type']").value("DynamicHttpMetadataResolver")) + + } + + @WithMockAdmin + def "GET multiple available MetadataResolvers -> /api/MetadataResolvers"() { + given: 'Two resolvers are available in data store' + def resolvers = [ + new DynamicHttpMetadataResolver().with { + it.name = 'Test DynamicHttpMetadataResolver' + it + }, + new FileBackedHttpMetadataResolver().with { + it.name = 'Test FileBackedHttpMetadataResolver' + it + } + ] + resolvers.each { + metadataResolverRepository.save(it) + } + + when: 'GET request is made' + def result = mockMvc.perform(get(BASE_URI)) + + then: + result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.[0].name").value("Test DynamicHttpMetadataResolver")) + .andExpect(jsonPath("\$.[0].['@type']").value("DynamicHttpMetadataResolver")) + .andExpect(jsonPath("\$.[1].name").value("Test FileBackedHttpMetadataResolver")) + .andExpect(jsonPath("\$.[1].['@type']").value("FileBackedHttpMetadataResolver")) + } + + @WithMockAdmin + def "GET concrete MetadataResolver -> /api/MetadataResolvers/{resourceId}"() { + given: 'One resolver is available in data store' + def resolver = new DynamicHttpMetadataResolver().with { + it.name = 'Test DynamicHttpMetadataResolver' + it + } + def resolverResourceId = resolver.resourceId + metadataResolverRepository.save(resolver) + + when: 'GET request is made with resource Id matching the existing resolver' + def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")) + + then: + result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.name").value("Test DynamicHttpMetadataResolver")) + .andExpect(jsonPath("\$.['@type']").value("DynamicHttpMetadataResolver")) + + } + + @WithMockAdmin + def "GET non-existent MetadataResolver -> /api/MetadataResolvers/{resourceId}"() { + when: 'GET request is made with resource Id not matching any resolvers' + def result = mockMvc.perform(get("$BASE_URI/bogus-resource-id")) + + then: + result.andExpect(status().isNotFound()) + } + + @WithMockAdmin + def "SHIBUI-839 - POST resolver with spaces in the provider name results in trimmed name"() { + given: + def resolver = generator.buildRandomMetadataResolverOfType('DynamicHttp') + resolver.name = ' This name has spaces ' + def expectedName = 'This name has spaces' + + when: + def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) + + then: + result.andExpect(status().isCreated()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.name").value(expectedName)) + } + + @WithMockAdmin + @Unroll + def "POST new concrete MetadataResolver of type #resolverType -> /api/MetadataResolvers"(String resolverType) { + given: 'New MetadataResolver JSON representation' + def resolver = generator.buildRandomMetadataResolverOfType(resolverType) + String sourceDirectory + if (resolverType == 'LocalDynamic') { + sourceDirectory = ((LocalDynamicMetadataResolver) resolver).sourceDirectory + } + + when: 'POST request is made with new Resolver JSON representation' + def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) + + then: + result.andExpect(status().isCreated()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.['@type']").value(resolver.getType())) + + cleanup: + if (sourceDirectory != null) { + def tmpDirectory = new File(sourceDirectory) + if (tmpDirectory.exists()) { + tmpDirectory.deleteDir() + } + } + + where: + resolverType | _ + 'DynamicHttp' | _ + 'FileBacked' | _ + 'LocalDynamic' | _ + 'ResourceBacked' | _ + 'Filesystem' | _ + } + + @WithMockAdmin + def "SHIBUI-1992 - error creating FileBackedHTTPMetadata"() { + def resolver = new FileBackedHttpMetadataResolver().with { + it.name = 'FBHMR' + it.xmlId = '1' + it.backingFile = 'tmp/foo' + it.metadataURL = 'https://nexus.microsoftonline-p.com/federationmetadata/saml20/federationmetadata.xml' + it.backupFileInitNextRefreshDelay = 'PT4H' + it.enabled = Boolean.FALSE + it + } + + when: + def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) + + then: + result.andExpect(status().isCreated()) + } + + @WithMockAdmin + @Unroll + def "PUT concrete MetadataResolver of type #resolverType with updated changes -> /api/MetadataResolvers/{resourceId}"(String resolverType) { + given: 'One resolver is available in data store' + def resolver = generator.buildRandomMetadataResolverOfType(resolverType) + String sourceDirectory + if (resolverType == 'Localdynamic') { + sourceDirectory = ((LocalDynamicMetadataResolver) resolver).sourceDirectory + } + def resolverResourceId = resolver.resourceId + metadataResolverRepository.save(resolver) + + when: 'GET request is made with resource Id matching the existing resolver' + def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn() + + and: 'Resolver data is updated and sent back to the server' + def metadataResolverMap = new JsonSlurper().parseText(result.getResponse().getContentAsString()) + + metadataResolverMap.name = 'Updated Resolver Name' + def updatedResult = mockMvc.perform(put("$BASE_URI/${metadataResolverMap.resourceId}").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(metadataResolverMap))) + + then: + updatedResult.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) + .andExpect(jsonPath("\$.name").value('Updated Resolver Name')) + + cleanup: + if (sourceDirectory != null) { + def tmpDirectory = new File(sourceDirectory) + if (tmpDirectory.exists()) { + tmpDirectory.deleteDir() + } + } + + where: + resolverType | _ + 'DynamicHttp' | _ + 'FileBacked' | _ + 'LocalDynamic' | _ + 'ResourceBacked' | _ + 'Filesystem' | _ + } + + @WithMockAdmin + def "PUT concrete MetadataResolver with version conflict -> /api/MetadataResolvers/{resourceId}"() { + given: 'One resolver is available in data store' + def resolver = new DynamicHttpMetadataResolver().with { + it.name = 'DynamicHTTP' + it.xmlId = 'DynamicHTTP' + it.metadataRequestURLConstructionScheme = new MetadataQueryProtocolScheme().with { + it.transformRef = 'transformRef' + it.content = 'content' + it + } + it + } + def resolverResourceId = resolver.resourceId + def persistedResolver = metadataResolverRepository.save(resolver) + + when: 'GET request is made with resource Id matching the existing resolver' + MvcResult result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn() + + and: 'Resolver data is updated and sent back to the server, but then original resolver is changed in data store' + persistedResolver.name = 'Some other name' + metadataResolverRepository.save(persistedResolver) + + def metadataResolverMap = mapper.readValue(result.getResponse().getContentAsString(), DynamicHttpMetadataResolver.class) + metadataResolverMap.name = 'Updated DynamicHttpMetadataResolver' + def updatedResult = mockMvc.perform(put("$BASE_URI/${metadataResolverMap.resourceId}").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(metadataResolverMap))) + + then: + updatedResult.andExpect(status().isConflict()) + } + + @WithMockAdmin + def "POST new MetadataResolver with one EntityAttributesFilters attached -> /api/MetadataResolvers"() { + given: 'New MetadataResolver with attached entity attributes filter JSON representation' + def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') + resolver.metadataFilters << generator.entityAttributesFilter() + + when: 'POST request is made with new FileBackedMetadataResolver with EntityAttributesFilter JSON representation' + def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) + + then: + def location = result.andExpect(status().isCreated()).andReturn().getResponse().getHeaderValue("Location") + + location.contains(BASE_URI) + + when: 'Query REST API for newly created resolver' + def createdResolverResult = mockMvc.perform(get(location)).andReturn().getResponse().getContentAsString() + def createdResolver = mapper.readValue(createdResolverResult, edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver) + + then: + createdResolver.metadataFilters.size() == 1 + createdResolver.metadataFilters[0] instanceof EntityAttributesFilter } -// @WithMockAdmin -// def "GET one available MetadataResolver -> /api/MetadataResolvers"() { -// given: 'One resolver is available in data store' -// def resolver = new DynamicHttpMetadataResolver().with { -// it.name = 'Test DynamicHttpMetadataResolver' -// it -// } -// metadataResolverRepository.save(resolver) -// -// when: 'GET request is made' -// def result = mockMvc.perform(get(BASE_URI)) -// -// then: -// result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.[0].name").value("Test DynamicHttpMetadataResolver")) -// .andExpect(jsonPath("\$.[0].['@type']").value("DynamicHttpMetadataResolver")) -// -// } -// -// @WithMockAdmin -// def "GET multiple available MetadataResolvers -> /api/MetadataResolvers"() { -// given: 'Two resolvers are available in data store' -// def resolvers = [ -// new DynamicHttpMetadataResolver().with { -// it.name = 'Test DynamicHttpMetadataResolver' -// it -// }, -// new FileBackedHttpMetadataResolver().with { -// it.name = 'Test FileBackedHttpMetadataResolver' -// it -// } -// ] -// resolvers.each { -// metadataResolverRepository.save(it) -// } -// -// when: 'GET request is made' -// def result = mockMvc.perform(get(BASE_URI)) -// -// then: -// result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.[0].name").value("Test DynamicHttpMetadataResolver")) -// .andExpect(jsonPath("\$.[0].['@type']").value("DynamicHttpMetadataResolver")) -// .andExpect(jsonPath("\$.[1].name").value("Test FileBackedHttpMetadataResolver")) -// .andExpect(jsonPath("\$.[1].['@type']").value("FileBackedHttpMetadataResolver")) -// } -// -// @WithMockAdmin -// def "GET concrete MetadataResolver -> /api/MetadataResolvers/{resourceId}"() { -// given: 'One resolver is available in data store' -// def resolver = new DynamicHttpMetadataResolver().with { -// it.name = 'Test DynamicHttpMetadataResolver' -// it -// } -// def resolverResourceId = resolver.resourceId -// metadataResolverRepository.save(resolver) -// -// when: 'GET request is made with resource Id matching the existing resolver' -// def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")) -// -// then: -// result.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.name").value("Test DynamicHttpMetadataResolver")) -// .andExpect(jsonPath("\$.['@type']").value("DynamicHttpMetadataResolver")) -// -// } -// -// @WithMockAdmin -// def "GET non-existent MetadataResolver -> /api/MetadataResolvers/{resourceId}"() { -// when: 'GET request is made with resource Id not matching any resolvers' -// def result = mockMvc.perform(get("$BASE_URI/bogus-resource-id")) -// -// then: -// result.andExpect(status().isNotFound()) -// } -// -// @WithMockAdmin -// def "SHIBUI-839 - POST resolver with spaces in the provider name results in trimmed name"() { -// given: -// def resolver = generator.buildRandomMetadataResolverOfType('DynamicHttp') -// resolver.name = ' This name has spaces ' -// def expectedName = 'This name has spaces' -// -// when: -// def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) -// -// then: -// result.andExpect(status().isCreated()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.name").value(expectedName)) -// } -// -// @WithMockAdmin -// @Unroll -// def "POST new concrete MetadataResolver of type #resolverType -> /api/MetadataResolvers"(String resolverType) { -// given: 'New MetadataResolver JSON representation' -// def resolver = generator.buildRandomMetadataResolverOfType(resolverType) -// String sourceDirectory -// if (resolverType == 'LocalDynamic') { -// sourceDirectory = ((LocalDynamicMetadataResolver) resolver).sourceDirectory -// } -// -// when: 'POST request is made with new Resolver JSON representation' -// def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) -// -// then: -// result.andExpect(status().isCreated()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.['@type']").value(resolver.getType())) -// -// cleanup: -// if (sourceDirectory != null) { -// def tmpDirectory = new File(sourceDirectory) -// if (tmpDirectory.exists()) { -// tmpDirectory.deleteDir() -// } -// } -// -// where: -// resolverType | _ -// 'DynamicHttp' | _ -// 'FileBacked' | _ -// 'LocalDynamic' | _ -// 'ResourceBacked' | _ -// 'Filesystem' | _ -// } -// -// @WithMockAdmin -// def "SHIBUI-1992 - error creating FileBackedHTTPMetadata"() { -// def resolver = new FileBackedHttpMetadataResolver().with { -// it.name = 'FBHMR' -// it.xmlId = '1' -// it.backingFile = 'tmp/foo' -// it.metadataURL = 'https://nexus.microsoftonline-p.com/federationmetadata/saml20/federationmetadata.xml' -// it.backupFileInitNextRefreshDelay = 'PT4H' -// it.enabled = Boolean.FALSE -// it -// } -// -// when: -// def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) -// -// then: -// result.andExpect(status().isCreated()) -// } -// -// @WithMockAdmin -// @Unroll -// def "PUT concrete MetadataResolver of type #resolverType with updated changes -> /api/MetadataResolvers/{resourceId}"(String resolverType) { -// given: 'One resolver is available in data store' -// def resolver = generator.buildRandomMetadataResolverOfType(resolverType) -// String sourceDirectory -// if (resolverType == 'Localdynamic') { -// sourceDirectory = ((LocalDynamicMetadataResolver) resolver).sourceDirectory -// } -// def resolverResourceId = resolver.resourceId -// metadataResolverRepository.save(resolver) -// -// when: 'GET request is made with resource Id matching the existing resolver' -// def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn() -// -// and: 'Resolver data is updated and sent back to the server' -// def metadataResolverMap = new JsonSlurper().parseText(result.getResponse().getContentAsString()) -// -// metadataResolverMap.name = 'Updated Resolver Name' -// def updatedResult = mockMvc.perform(put("$BASE_URI/${metadataResolverMap.resourceId}").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(metadataResolverMap))) -// -// then: -// updatedResult.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON)) -// .andExpect(jsonPath("\$.name").value('Updated Resolver Name')) -// -// cleanup: -// if (sourceDirectory != null) { -// def tmpDirectory = new File(sourceDirectory) -// if (tmpDirectory.exists()) { -// tmpDirectory.deleteDir() -// } -// } -// -// where: -// resolverType | _ -// 'DynamicHttp' | _ -// 'FileBacked' | _ -// 'LocalDynamic' | _ -// 'ResourceBacked' | _ -// 'Filesystem' | _ -// } -// -// @WithMockAdmin -// def "PUT concrete MetadataResolver with version conflict -> /api/MetadataResolvers/{resourceId}"() { -// given: 'One resolver is available in data store' -// def resolver = new DynamicHttpMetadataResolver().with { -// it.name = 'DynamicHTTP' -// it.xmlId = 'DynamicHTTP' -// it.metadataRequestURLConstructionScheme = new MetadataQueryProtocolScheme().with { -// it.transformRef = 'transformRef' -// it.content = 'content' -// it -// } -// it -// } -// def resolverResourceId = resolver.resourceId -// def persistedResolver = metadataResolverRepository.save(resolver) -// -// when: 'GET request is made with resource Id matching the existing resolver' -// MvcResult result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn() -// -// and: 'Resolver data is updated and sent back to the server, but then original resolver is changed in data store' -// persistedResolver.name = 'Some other name' -// metadataResolverRepository.save(persistedResolver) -// -// def metadataResolverMap = mapper.readValue(result.getResponse().getContentAsString(), DynamicHttpMetadataResolver.class) -// metadataResolverMap.name = 'Updated DynamicHttpMetadataResolver' -// def updatedResult = mockMvc.perform(put("$BASE_URI/${metadataResolverMap.resourceId}").contentType(APPLICATION_JSON).content(mapper.writeValueAsString(metadataResolverMap))) -// -// then: -// updatedResult.andExpect(status().isConflict()) -// } -// -// @WithMockAdmin -// def "POST new MetadataResolver with one EntityAttributesFilters attached -> /api/MetadataResolvers"() { -// given: 'New MetadataResolver with attached entity attributes filter JSON representation' -// def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') -// resolver.metadataFilters << generator.entityAttributesFilter() -// -// when: 'POST request is made with new FileBackedMetadataResolver with EntityAttributesFilter JSON representation' -// def result = mockMvc.perform(post(BASE_URI).contentType(APPLICATION_JSON).content(mapper.writeValueAsString(resolver))) -// -// then: -// def location = result.andExpect(status().isCreated()).andReturn().getResponse().getHeaderValue("Location") -// -// location.contains(BASE_URI) -// -// when: 'Query REST API for newly created resolver' -// def createdResolverResult = mockMvc.perform(get(location)).andReturn().getResponse().getContentAsString() -// def createdResolver = mapper.readValue(createdResolverResult, edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver) -// -// then: -// createdResolver.metadataFilters.size() == 1 -// createdResolver.metadataFilters[0] instanceof EntityAttributesFilter -// } - -// @WithMockAdmin -// @Transactional -// def "PUT MetadataResolver with one EntityAttributesFilters attached and check version -> /api/MetadataResolvers"() { -// given: 'MetadataResolver with attached entity attributes is available in data store' -// def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') -// resolver.metadataFilters << generator.entityAttributesFilter() -// def resolverResourceId = resolver.resourceId -// metadataResolverRepository.save(resolver) -// -// when: 'GET request is made with resource Id matching the existing resolver' -// def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn().getResponse().getContentAsString() -// def existingMetadataResolverMap = new JsonSlurper().parseText(result) -// -// and: 'PUT call is made with' -// existingMetadataResolverMap.name = 'Updated' -// def updatedResultFromPUT = mockMvc.perform(put("$BASE_URI/${existingMetadataResolverMap.resourceId}") -// .contentType(APPLICATION_JSON).content(mapper.writeValueAsString(existingMetadataResolverMap))) -// .andReturn().getResponse().getContentAsString() -// def updatedResultFromGET = mockMvc.perform(get("$BASE_URI/$existingMetadataResolverMap.resourceId")).andReturn().getResponse().getContentAsString() -// -// then: -// updatedResultFromPUT == updatedResultFromGET -// } + @WithMockAdmin + @Transactional + def "PUT MetadataResolver with one EntityAttributesFilters attached and check version -> /api/MetadataResolvers"() { + given: 'MetadataResolver with attached entity attributes is available in data store' + def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') + resolver.metadataFilters << generator.entityAttributesFilter() + def resolverResourceId = resolver.resourceId + metadataResolverRepository.save(resolver) + + when: 'GET request is made with resource Id matching the existing resolver' + def result = mockMvc.perform(get("$BASE_URI/$resolverResourceId")).andReturn().getResponse().getContentAsString() + def existingMetadataResolverMap = new JsonSlurper().parseText(result) + + and: 'PUT call is made with' + existingMetadataResolverMap.name = 'Updated' + def updatedResultFromPUT = mockMvc.perform(put("$BASE_URI/${existingMetadataResolverMap.resourceId}") + .contentType(APPLICATION_JSON).content(mapper.writeValueAsString(existingMetadataResolverMap))) + .andReturn().getResponse().getContentAsString() + def updatedResultFromGET = mockMvc.perform(get("$BASE_URI/$existingMetadataResolverMap.resourceId")).andReturn().getResponse().getContentAsString() + + then: + updatedResultFromPUT == updatedResultFromGET + } @TestConfiguration - static class LocalConfig { + private static class MRCILocalConfig { @Bean - MetadataResolver metadataResolver() { - new OpenSamlChainingMetadataResolver() + public AttributeUtility attributeUtility(OpenSamlObjects openSamlObjects) { + return new AttributeUtility(openSamlObjects); } @Bean - MetadataResolverConverterService metadataResolverConverterServiceImpl(IndexWriterService indexWriterService, OpenSamlObjects openSamlObjects) { - MetadataResolverConverterService result = new MetadataResolverConverterServiceImpl().with({ - it.indexWriterService = indexWriterService - it.openSamlObjects = openSamlObjects - it - }) - return result + DirectoryService directoryService() { + return new DirectoryServiceImpl() } @Bean JPAMetadataResolverServiceImpl jpaMetadataResolverService(MetadataResolver metadataResolver, MetadataResolverRepository metadataResolverRepository, OpenSamlObjects openSamlObjects, MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService, - ShibUIConfiguration shibUIConfiguration){ + ShibUIConfiguration shibUIConfiguration) { return new JPAMetadataResolverServiceImpl().with { it.metadataResolver = metadataResolver it.metadataResolverRepository = metadataResolverRepository @@ -433,15 +395,14 @@ class MetadataResolversControllerIntegrationTests extends Specification { } @Bean - @Primary - GroupServiceForTesting groupServiceForTesting(GroupsRepository repo, OwnershipRepository ownershipRepository) { - GroupServiceForTesting result = new GroupServiceForTesting(new GroupServiceImpl().with { - it.groupRepository = repo - it.ownershipRepository = ownershipRepository - return it - }) - result.ensureAdminGroupExists() - return result + public ModelRepresentationConversions modelRepresentationConversions(CustomPropertiesConfiguration customPropertiesConfiguration) { + return new ModelRepresentationConversions(customPropertiesConfiguration); + } + + @Bean + public MetadataResolversPositionOrderContainerService metadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository positionOrderContainerRepository, + MetadataResolverRepository resolverRepository) { + return new DefaultMetadataResolversPositionOrderContainerService(positionOrderContainerRepository, resolverRepository); } } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index 0cde7c38b..80b3fad04 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -112,7 +112,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { @TestConfiguration @Profile("local") - static class LocalConfig { + private static class LocalConfig { @Autowired OpenSamlObjects openSamlObjects diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests2.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests2.groovy index 906bc4a19..18532a57a 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests2.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests2.groovy @@ -37,40 +37,40 @@ import spock.lang.Specification @DirtiesContext @ActiveProfiles(value="local") class JPAEntityDescriptorServiceImplTests2 extends Specification { - + @Autowired GroupServiceForTesting groupService @Autowired RoleRepository roleRepository - + @Autowired JPAEntityDescriptorServiceImpl entityDescriptorService - + @Autowired UserRepository userRepository @Autowired UserService userService - + @Transactional def setup() { // ensure we start fresh with only expected users and roles and groups userRepository.deleteAll() roleRepository.deleteAll() groupService.clearAllForTesting() - + Group ga = new Group() ga.setResourceId("testingGroup") ga.setName("Group A") groupService.createGroup(ga) - + Group gb = new Group() gb.setResourceId("testingGroupBBB") gb.setName("Group BBB") gb.setValidationRegex("^(?:https?:\\/\\/)?(?:[^.]+\\.)?shib\\.org(\\/.*)?\$") gb = groupService.createGroup(gb) - + def roles = [new Role().with { name = 'ROLE_ADMIN' it @@ -83,12 +83,12 @@ class JPAEntityDescriptorServiceImplTests2 extends Specification { }] roles.each { roleRepository.save(it) - } - + } + Optional adminRole = roleRepository.findByName("ROLE_ADMIN") User adminUser = new User(username: "admin", roles: [adminRole.get()], password: "foo") userService.save(adminUser) - + Optional userRole = roleRepository.findByName("ROLE_USER") User user = new User(username: "someUser", roles:[userRole.get()], password: "foo", group: gb) userService.save(user) @@ -105,14 +105,14 @@ class JPAEntityDescriptorServiceImplTests2 extends Specification { def expectedSpName = 'sp1' def expectedUUID = 'uuid-1' def entityDescriptor = new EntityDescriptor(resourceId: expectedUUID, entityID: expectedEntityId, serviceProviderName: expectedSpName, serviceEnabled: false) - + when: def result = entityDescriptorService.createNew(entityDescriptor) - + then: ((EntityDescriptorRepresentation)result).getIdOfOwner() == "testingGroupBBB" } - + @TestConfiguration @Profile("local") static class LocalConfig { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index e91f3fbab..b6092c8e3 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -486,7 +486,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { @TestConfiguration @Profile("local") - static class Config { + private static class Config { @Autowired OpenSamlObjects openSamlObjects @@ -526,4 +526,4 @@ class JPAMetadataResolverServiceImplTests extends Specification { } } } -} +} \ No newline at end of file