diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index efd10c308..90954dfb8 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -40,7 +40,7 @@ public class JPAMetadataResolverServiceImpl implements MetadataResolverService { // MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().stream().filter(r -> r.getId().equals(metadataResolverName)).findFirst().get(); MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find { it.id == metadataResolverName } - edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver jpaMetadataResolver = metadataResolverRepository.findByName(metadataResolverName); + edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver jpaMetadataResolver = metadataResolverRepository.findByName(metadataResolverName); if (targetMetadataResolver && targetMetadataResolver.getMetadataFilter() instanceof MetadataFilterChain) { MetadataFilterChain metadataFilterChain = (MetadataFilterChain)targetMetadataResolver.getMetadataFilter(); @@ -88,7 +88,7 @@ public class JPAMetadataResolverServiceImpl implements MetadataResolverService { 'xsi:type': 'ChainingMetadataProvider', 'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd' ) { - metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver mr -> + metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> MetadataProvider(id: 'HTTPMetadata', 'xsi:type': 'FileBackedHTTPMetadataProvider', backingFile: '%{idp.home}/metadata/incommonmd.xml', diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java index f6a88b27d..51ba89b05 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java @@ -95,7 +95,7 @@ protected void processConditionalRetrievalHeaders(HttpResponse response) { resolvers.add(incommonMR); if (!metadataResolverRepository.findAll().iterator().hasNext()) { - edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver(); + edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver(); mr.setName("incommonmd"); metadataResolverRepository.save(mr); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java new file mode 100644 index 000000000..5b8000482 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderController.java @@ -0,0 +1,110 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@RestController +@RequestMapping("/api/MetadataProvider/FileBackedHttp") +public class FileBackedHttpMetadataProviderController { + private static final Logger logger = LoggerFactory.getLogger(FileBackedHttpMetadataProviderController.class); + + @Autowired + FileBackedHttpMetadataResolverRepository repository; + + @DeleteMapping("/{resourceId}") + public ResponseEntity deleteByResourceId(@PathVariable String resourceId) { + if (repository.deleteByResourceId(resourceId)) { + return ResponseEntity.accepted().build(); + } else { + return ResponseEntity.notFound().build(); + } + } + + @GetMapping("/name/{metadataProviderName}") + @Transactional(readOnly = true) + public ResponseEntity getOneByName(@PathVariable String metadataProviderName) { + FileBackedHttpMetadataResolver resolver = repository.findByName(metadataProviderName); + if (resolver == null) { + return ResponseEntity.notFound().build(); + } else { + resolver.setVersion(resolver.hashCode()); + return ResponseEntity.ok(resolver); + } + } + + @GetMapping("/{resourceId}") + @Transactional(readOnly = true) + public ResponseEntity getOneByResourceId(@PathVariable String resourceId) { + FileBackedHttpMetadataResolver resolver = repository.findByResourceId(resourceId); + if (resolver == null) { + return ResponseEntity.notFound().build(); + } else { + resolver.setVersion(resolver.hashCode()); + return ResponseEntity.ok(resolver); + } + } + + @PostMapping + public ResponseEntity create(@RequestBody FileBackedHttpMetadataResolver resolver) { + if (repository.findByName(resolver.getName()) != null) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + FileBackedHttpMetadataResolver persistedResolver = repository.save(resolver); + persistedResolver.setVersion(persistedResolver.hashCode()); + + return ResponseEntity + .created(getResourceUriFor(persistedResolver)) + .body(persistedResolver); + } + + @PutMapping + public ResponseEntity update(@RequestBody FileBackedHttpMetadataResolver resolver) { + FileBackedHttpMetadataResolver existingResolver = repository.findByResourceId(resolver.getResourceId()); + + if (existingResolver == null) { + return ResponseEntity.notFound().build(); + } + + if (existingResolver.hashCode() != resolver.getVersion()) { + logger.info("Comparing: " + existingResolver.hashCode() + " with " + resolver.getVersion()); + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + resolver.setAudId(existingResolver.getAudId()); + //TODO: Do we need to set anything else? dates? + + FileBackedHttpMetadataResolver updatedResolver = repository.save(resolver); + updatedResolver.setVersion(updatedResolver.hashCode()); + + return ResponseEntity.ok(updatedResolver); + } + + private static URI getResourceUriFor(FileBackedHttpMetadataResolver resolver) { + return ServletUriComponentsBuilder + .fromCurrentServletMapping().path("/api/MetadataProvider/FileBackedHttp/") + .pathSegment(resolver.getResourceId()) + .build() + .toUri(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java index c065d4771..c7af8c0c6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java @@ -2,8 +2,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; -import edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.service.FilterService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; @@ -13,7 +13,13 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.net.URI; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java deleted file mode 100644 index 02a772237..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java +++ /dev/null @@ -1,56 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.domain; - -import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; -import lombok.EqualsAndHashCode; - -import javax.persistence.CascadeType; -import javax.persistence.Entity; -import javax.persistence.OneToMany; -import javax.persistence.OrderColumn; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -@Entity -@EqualsAndHashCode(callSuper = true) -public class MetadataResolver extends AbstractAuditable { - private String name; - private String resourceId = UUID.randomUUID().toString(); - - @OneToMany(cascade = CascadeType.ALL) - @OrderColumn - private List metadataFilters = new ArrayList<>(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getResourceId() { - return resourceId; - } - - public void setResourceId(String resourceId) { - this.resourceId = resourceId; - } - - public List getMetadataFilters() { - return metadataFilters; - } - - public void setMetadataFilters(List metadataFilters) { - this.metadataFilters = metadataFilters; - } - - @Override - public String toString() { - return "MetadataResolver{\n" + - "name='" + name + "\'\n" + - ", resourceId='" + resourceId + "\'\n" + - ", metadataFilters=\n" + metadataFilters + - '}'; - } -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java new file mode 100644 index 000000000..c9850ecc6 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java @@ -0,0 +1,26 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Embedded; +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Getter +@Setter +@ToString +public class FileBackedHttpMetadataResolver extends MetadataResolver { + + @Embedded + private ReloadableMetadataResolverAttributes reloadableMetadataResolverAttributes; + + @Embedded + private HttpMetadataResolverAttributes httpMetadataResolverAttributes; + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/HttpMetadataResolverAttributes.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/HttpMetadataResolverAttributes.java new file mode 100644 index 000000000..437516ffd --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/HttpMetadataResolverAttributes.java @@ -0,0 +1,58 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Enumerated; + +import static javax.persistence.EnumType.STRING; + +@Embeddable +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +public class HttpMetadataResolverAttributes { + + private String httpClientRef; + + private String connectionRequestTimeout; + + private String requestTimeout; + + private String socketTimeout; + + private Boolean disregardTLSCertificate; + + private String tlsTrustEngineRef; + + private String httpClientSecurityParametersRef; + + private String proxyHost; + + private String proxyPort; + + private String proxyUser; + + private String proxyPassword; + + @Enumerated(STRING) + @Column(length = 6) + private HttpCachingType httpCaching; + + private String httpCacheDirectory; + + private Integer httpMaxCacheEntries; + + private Integer httpMaxCacheEntrySize; + + private enum HttpCachingType { + none,file,memory + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java new file mode 100644 index 000000000..ded0edd05 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -0,0 +1,56 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; +import javax.persistence.Transient; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@EqualsAndHashCode(callSuper = true, exclude={"version"}) +@NoArgsConstructor +@Getter +@Setter +@ToString +public class MetadataResolver extends AbstractAuditable { + + @Column(unique=true) + private String name; + + @Column(unique=true) + private String resourceId = UUID.randomUUID().toString(); + + private Boolean requireValidMetadata; + + private Boolean failFastInitialization; + + private Integer sortKey; + + private String criterionPredicateRegistryRef; + + private Boolean useDefaultPredicateRegistry; + + private Boolean satisfyAnyPredicates; + + @OneToMany(cascade = CascadeType.ALL) + @OrderColumn + private List metadataFilters = new ArrayList<>(); + + @Transient + private int version; +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ReloadableMetadataResolverAttributes.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ReloadableMetadataResolverAttributes.java new file mode 100644 index 000000000..cddf5b935 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ReloadableMetadataResolverAttributes.java @@ -0,0 +1,35 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Embeddable; + +@Embeddable +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +public class ReloadableMetadataResolverAttributes { + + private String parserPoolRef; + + private String taskTimerRef; + + private String minRefreshDelay; + + private String maxRefreshDelay; + + private Double refreshDelayFactor; + + private String indexesRef; + + private Boolean resolveViaPredicatesOnly; + + private String expirationWarningThreshold; + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepository.java new file mode 100644 index 000000000..97d069173 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepository.java @@ -0,0 +1,14 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver; +import org.springframework.data.repository.CrudRepository; + +/** + * Spring Data CRUD repository for instances of {@link FileBackedHttpMetadataResolver}s. + */ +public interface FileBackedHttpMetadataResolverRepository extends CrudRepository { + + FileBackedHttpMetadataResolver findByName(String name); + boolean deleteByResourceId(String resourceId); + FileBackedHttpMetadataResolver findByResourceId(String resourceId); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java index 5dba01872..fc415aef1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java @@ -1,10 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.repository; -import edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import org.springframework.data.repository.CrudRepository; /** - * Repository to manage {@link edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver} instances. + * Repository to manage {@link MetadataResolver} instances. */ public interface MetadataResolverRepository extends CrudRepository { MetadataResolver findByName(String name); diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy new file mode 100644 index 000000000..1cf30ab69 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy @@ -0,0 +1,591 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.repository.FileBackedHttpMetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +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.test.web.servlet.setup.MockMvcBuilders +import spock.lang.Specification + +import static org.hamcrest.CoreMatchers.containsString +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class FileBackedHttpMetadataProviderControllerTests extends Specification { + RandomGenerator randomGenerator + ObjectMapper mapper + + def repository = Mock(FileBackedHttpMetadataResolverRepository) + def controller + def mockMvc + + def setup() { + randomGenerator = new RandomGenerator() + mapper = new ObjectMapper() + + controller = new FileBackedHttpMetadataProviderController ( + repository: repository + ) + + mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + } + + def "DELETE deletes the desired resolver"() { + given: + def randomResourceId = randomGenerator.randomId() + + 1 * repository.deleteByResourceId(randomResourceId) >> true + + when: + def result = mockMvc.perform( + delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) + + then: + result.andExpect(status().isAccepted()) + } + + def "DELETE returns error when desired resolver is not found"() { + given: + def randomResourceId = randomGenerator.randomId() + + 1 * repository.deleteByResourceId(randomResourceId) >> false + + when: + def result = mockMvc.perform( + delete("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "POST a new resolver properly persists and returns the new persisted resolver"() { + given: + def postedJsonBody = '''{ + "name": "name", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}''' + def resolver = new ObjectMapper().readValue(postedJsonBody.bytes, FileBackedHttpMetadataResolver) + 1 * repository.findByName(resolver.getName()) >> null + 1 * repository.save(_) >> resolver + + def expectedResolverUUID = resolver.getResourceId() + def expectedResponseHeader = 'Location' + def expectedResponseHeaderValue = "/api/MetadataProvider/FileBackedHttp/$expectedResolverUUID" + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/FileBackedHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isCreated()) + .andExpect(content().json(postedJsonBody, false)) + .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) + + } + + def "POST a new resolver that has a name of a persisted resolver returns conflict"() { + given: + def randomResolverName = randomGenerator.randomString(10) + def postedJsonBody = """{ + "name": "$randomResolverName", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + def resolver = new ObjectMapper().readValue(postedJsonBody.bytes, FileBackedHttpMetadataResolver) + 1 * repository.findByName(randomResolverName) >> resolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/FileBackedHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isConflict()) + } + + def "GET by resourceId returns the desired persisted resolver"() { + given: + def randomUUID = randomGenerator.randomId() + def resolverJson = """{ + "name": "name", + "resourceId": "$randomUUID", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + + def resolver = new ObjectMapper().readValue(resolverJson.bytes, FileBackedHttpMetadataResolver) + resolver.setResourceId(randomUUID) + + 1 * repository.findByResourceId(randomUUID) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/FileBackedHttp/$randomUUID")) + + then: + result.andExpect(status().isOk()) + .andExpect(content().contentType(expectedResponseContentType)) + .andExpect(content().json(resolverJson, false)) + } + + def "GET by unknown resource id returns not found"() { + given: + def randomResourceId = randomGenerator.randomId() + + 1 * repository.findByResourceId(randomResourceId) >> null + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/FileBackedHttp/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "GET by resolver name returns the desired persisted resolver"() { + given: + def randomResolverName = randomGenerator.randomString(10) + def resolverJson = """{ + "name": "$randomResolverName", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + + def resolver = new ObjectMapper().readValue(resolverJson.bytes, FileBackedHttpMetadataResolver) + + 1 * repository.findByName(randomResolverName) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) + + then: + result.andExpect(status().isOk()) + .andExpect(content().contentType(expectedResponseContentType)) + .andExpect(content().json(resolverJson, false)) + } + + def "GET by unknown resolver name returns not found"() { + given: + def randomResolverName = randomGenerator.randomString(10) + + 1 * repository.findByName(randomResolverName) >> null + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/FileBackedHttp/name/$randomResolverName")) + + then: + result.andExpect(status().isNotFound()) + } + + def "PUT allows for a successful update of an already-persisted resolver"() { + given: + def randomResourceId = "resourceId" + def existingResolverJson = """{ + "name": "name", + "resourceId": "$randomResourceId", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + def existingResolver = new ObjectMapper().readValue(existingResolverJson.bytes, FileBackedHttpMetadataResolver) + def existingResolverVersion = existingResolver.hashCode() + + def randomName = randomGenerator.randomString(10) + def postedJsonBody = """{ + "name": "$randomName", + "resourceId": "$randomResourceId", + "version": "$existingResolverVersion", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + def updatedResolver = new ObjectMapper().readValue(postedJsonBody.bytes, FileBackedHttpMetadataResolver) + 1 * repository.findByResourceId(randomResourceId) >> existingResolver + 1 * repository.save(_) >> updatedResolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/FileBackedHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + def expectedJson = new JsonSlurper().parseText(postedJsonBody) + expectedJson << [version: updatedResolver.hashCode()] + result.andExpect(status().isOk()) + .andExpect(content().contentType(expectedResponseContentType)) + .andExpect(content().json(JsonOutput.toJson(expectedJson), false)) + } + + def "PUT of an updated resolver with an incorrect version returns a conflict"() { + given: + def randomResourceId = "resourceId" + def existingResolverJson = """{ + "name": "name", + "resourceId": "$randomResourceId", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + def existingResolver = new ObjectMapper().readValue(existingResolverJson.bytes, FileBackedHttpMetadataResolver) + def existingResolverVersion = existingResolver.hashCode() + + def randomName = randomGenerator.randomString(10) + def randomVersion = randomGenerator.randomInt() + def postedJsonBody = """{ + "name": "$randomName", + "resourceId": "$randomResourceId", + "version": "$randomVersion", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + 1 * repository.findByResourceId(randomResourceId) >> existingResolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/FileBackedHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isConflict()) + } + + def "PUT of a resolver that is not persisted returns not found"() { + given: + def randomResourceId = randomGenerator.randomId() + def postedJsonBody = """{ + "name": "name", + "resourceId": "$randomResourceId", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef", + "taskTimerRef": "taskTimerRef", + "minRefreshDelay": "minRefreshDelay", + "maxRefreshDelay": "maxRefreshDelay", + "refreshDelayFactor": 1.0, + "indexesRef": "indexesRef", + "resolveViaPredicatesOnly": true, + "expirationWarningThreshold": "expirationWarningThreshold" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}""" + 1 * repository.findByResourceId(randomResourceId) >> null + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/FileBackedHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isNotFound()) + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy index fdf90d65d..9d8a1f72c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy @@ -5,7 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfigurat import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter -import edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.service.FilterService import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy new file mode 100644 index 000000000..d0beb24d2 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FileBackedHttpMetadataResolverRepositoryTests.groovy @@ -0,0 +1,128 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ReloadableMetadataResolverAttributes +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.annotation.DirtiesContext +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +import javax.persistence.EntityManager + +import static edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY +import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes.HttpCachingType.memory + +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD) +class FileBackedHttpMetadataResolverRepositoryTests extends Specification { + + @Autowired + FileBackedHttpMetadataResolverRepository repositoryUnderTest + + @Autowired + EntityManager entityManager + + def "file backed http metadata resolver instances persist OK"() { + when: + def mdr = new FileBackedHttpMetadataResolver().with { + it.name = "FileBackedHttpMetadata" + + it.httpMetadataResolverAttributes = new HttpMetadataResolverAttributes().with { + it.connectionRequestTimeout = "PT05" + it.disregardTLSCertificate = true + it.httpCaching = memory + it + } + it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes().with { + it.indexesRef = "indexesSpringBeanId" + it + } + + it.metadataFilters.add(new EntityAttributesFilter().with { + it.entityAttributesFilterTarget = new EntityAttributesFilterTarget().with { + it.entityAttributesFilterTargetType = ENTITY + it.setValue(["hola"]) + it + } + it + }) + it + } + repositoryUnderTest.save(mdr) + + then: + repositoryUnderTest.findAll().size() > 0 + FileBackedHttpMetadataResolver item = repositoryUnderTest.findByName("FileBackedHttpMetadata") + + item.name == "FileBackedHttpMetadata" + item.metadataFilters.size() == 1 + item.metadataFilters[0].entityAttributesFilterTarget.entityAttributesFilterTargetType == ENTITY + item.metadataFilters[0].entityAttributesFilterTarget.value.size() == 1 + item.metadataFilters[0].entityAttributesFilterTarget.value.get(0) == "hola" + item.httpMetadataResolverAttributes.connectionRequestTimeout == "PT05" + item.httpMetadataResolverAttributes.disregardTLSCertificate + item.httpMetadataResolverAttributes.httpCaching == memory + item.reloadableMetadataResolverAttributes.indexesRef == "indexesSpringBeanId" + } + + def "FileBackedHttpMetadataResolver hashcode works as desired"() { + given: + // TODO: There is weirdness here if reloadableMetadataResolverAttributes is empty. + // I suspect similar weirdness if httpMetadataResolverAttributes is an empty object, too. + def resolverJson = '''{ + "name": "name", + "requireValidMetadata": true, + "failFastInitialization": true, + "sortKey": 7, + "criterionPredicateRegistryRef": "criterionPredicateRegistryRef", + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": true, + "metadataFilters": [], + "reloadableMetadataResolverAttributes": { + "parserPoolRef": "parserPoolRef" + }, + "httpMetadataResolverAttributes": { + "httpClientRef": "httpClientRef", + "connectionRequestTimeout": "connectionRequestTimeout", + "requestTimeout": "requestTimeout", + "socketTimeout": "socketTimeout", + "disregardTLSCertificate": true, + "tlsTrustEngineRef": "tlsTrustEngineRef", + "httpClientSecurityParametersRef": "httpClientSecurityParametersRef", + "proxyHost": "proxyHost", + "proxyPort": "proxyPort", + "proxyUser": "proxyUser", + "proxyPassword": "proxyPassword", + "httpCaching": "none", + "httpCacheDirectory": "httpCacheDirectory", + "httpMaxCacheEntries": 1, + "httpMaxCacheEntrySize": 2 + } +}''' + + when: + def resolver = new ObjectMapper().readValue(resolverJson.bytes, FileBackedHttpMetadataResolver) + def persistedResolver = repositoryUnderTest.save(resolver) + entityManager.flush() + + then: + def item1 = repositoryUnderTest.findByResourceId(persistedResolver.resourceId) + entityManager.clear() + def item2 = repositoryUnderTest.findByResourceId(persistedResolver.resourceId) + + item1.hashCode() == item2.hashCode() + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy index 7e35f655e..76cb92e68 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy @@ -5,7 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConf import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilterTarget -import edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl 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 7d9ae3a81..7628f30de 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 @@ -97,7 +97,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { } if (!metadataResolverRepository.findAll().iterator().hasNext()) { - edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver() + edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver() mr.setName("incommonmd") metadataResolverRepository.save(mr) } 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 faa913f6b..afc93df62 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 @@ -1,7 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilterTarget @@ -75,7 +74,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { ''' when: - def mdr = new edu.internet2.tier.shibboleth.admin.ui.domain.MetadataResolver().with { + def mdr = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver().with { it.name = "testme" it.metadataFilters.add(new EntityAttributesFilter().with { it.entityAttributesFilterTarget = new EntityAttributesFilterTarget().with {