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 d24174234..34e73233c 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 @@ -1,13 +1,24 @@ package edu.internet2.tier.shibboleth.admin.ui.service; +import com.google.common.base.Predicate; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; + +import com.google.common.base.Predicate + import com.google.common.base.Predicate import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter + import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects + import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import groovy.util.logging.Slf4j import groovy.xml.DOMBuilder @@ -115,6 +126,52 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForResolver(DynamicHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { + markupBuilderDelegate.MetadataProvider(id: resolver.name, + 'xsi:type': 'DynamicHttpMetadataProvider', + requireValidMetadata: !resolver.requireValidMetadata ?: null, + failFastInitialization: !resolver.failFastInitialization ?: null, + sortKey: resolver.sortKey, + criterionPredicateRegistryRef: resolver.criterionPredicateRegistryRef, + useDefaultPredicateRegistry: !resolver.useDefaultPredicateRegistry ?: null, + satisfyAnyPredicates: resolver.satisfyAnyPredicates ?: null, + parserPoolRef: resolver.dynamicMetadataResolverAttributes?.parserPoolRef, + taskTimerRef: resolver.dynamicMetadataResolverAttributes?.taskTimerRef, + refreshDelayFactor: resolver.dynamicMetadataResolverAttributes?.refreshDelayFactor, + minCacheDuration: resolver.dynamicMetadataResolverAttributes?.minCacheDuration, + maxCacheDuration: resolver.dynamicMetadataResolverAttributes?.maxCacheDuration, + maxIdleEntityData: resolver.dynamicMetadataResolverAttributes?.maxIdleEntityData, + removeIdleEntityData: !resolver.dynamicMetadataResolverAttributes?.removeIdleEntityData ?: null, + cleanupTaskInterval: resolver.dynamicMetadataResolverAttributes?.cleanupTaskInterval, + persistentCacheManagerRef: resolver.dynamicMetadataResolverAttributes?.persistentCacheManagerRef, + persistentCacheManagerDirectory: resolver.dynamicMetadataResolverAttributes?.persistentCacheManagerDirectory, + persistentCacheKeyGeneratorRef: resolver.dynamicMetadataResolverAttributes?.persistentCacheKeyGeneratorRef, + initializeFromPersistentCacheInBackground: !resolver.dynamicMetadataResolverAttributes?.initializeFromPersistentCacheInBackground ?: null, + backgroundInitializationFromCacheDelay: resolver.dynamicMetadataResolverAttributes?.backgroundInitializationFromCacheDelay, + initializationFromCachePredicateRef: resolver.dynamicMetadataResolverAttributes?.initializationFromCachePredicateRef, + + maxConnectionsTotal: resolver.maxConnectionsTotal, + maxConnectionsPerRoute: resolver.maxConnectionsPerRoute, + supportedContentTypes: resolver.supportedContentTypes?.value, //not sure this is right. maybe take off the ?.value + + httpClientRef: resolver.httpMetadataResolverAttributes?.httpClientRef, + connectionRequestTimeout: resolver.httpMetadataResolverAttributes?.connectionRequestTimeout, + connectionTimeout: resolver.httpMetadataResolverAttributes?.connectionTimeout, + socketTimeout: resolver.httpMetadataResolverAttributes?.socketTimeout, + disregardTLSCertificate: resolver.httpMetadataResolverAttributes?.disregardTLSCertificate ?: null, + httpClientSecurityParametersRef: resolver.httpMetadataResolverAttributes?.httpClientSecurityParametersRef, + proxyHost: resolver.httpMetadataResolverAttributes?.proxyHost, + proxyPort: resolver.httpMetadataResolverAttributes?.proxyHost, + proxyUser: resolver.httpMetadataResolverAttributes?.proxyUser, + proxyPassword: resolver.httpMetadataResolverAttributes?.proxyPassword, + httpCaching: resolver.httpMetadataResolverAttributes?.httpCaching, + httpCacheDirectory: resolver.httpMetadataResolverAttributes?.httpCacheDirectory, + httpMaxCacheEntries: resolver.httpMetadataResolverAttributes?.httpMaxCacheEntries, + httpMaxCacheEntrySize: resolver.httpMetadataResolverAttributes?.httpMaxCacheEntrySize) { + + childNodes() + } + } void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) { markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') { @@ -182,4 +239,36 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { childNodes() } } + + void constructXmlNodeForResolver(LocalDynamicMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { + markupBuilderDelegate.MetadataProvider(sourceDirectory: resolver.sourceDirectory, + sourceManagerRef: resolver.sourceManagerRef, + sourceKeyGeneratorRef: resolver.sourceKeyGeneratorRef, + + id: resolver.name, + 'xsi:type': 'DynamicHttpMetadataProvider', + requireValidMetadata: !resolver.requireValidMetadata ?: null, + failFastInitialization: !resolver.failFastInitialization ?: null, + sortKey: resolver.sortKey, + criterionPredicateRegistryRef: resolver.criterionPredicateRegistryRef, + useDefaultPredicateRegistry: !resolver.useDefaultPredicateRegistry ?: null, + satisfyAnyPredicates: resolver.satisfyAnyPredicates ?: null, + parserPoolRef: resolver.dynamicMetadataResolverAttributes?.parserPoolRef, + taskTimerRef: resolver.dynamicMetadataResolverAttributes?.taskTimerRef, + refreshDelayFactor: resolver.dynamicMetadataResolverAttributes?.refreshDelayFactor, + minCacheDuration: resolver.dynamicMetadataResolverAttributes?.minCacheDuration, + maxCacheDuration: resolver.dynamicMetadataResolverAttributes?.maxCacheDuration, + maxIdleEntityData: resolver.dynamicMetadataResolverAttributes?.maxIdleEntityData, + removeIdleEntityData: !resolver.dynamicMetadataResolverAttributes?.removeIdleEntityData ?: null, + cleanupTaskInterval: resolver.dynamicMetadataResolverAttributes?.cleanupTaskInterval, + persistentCacheManagerRef: resolver.dynamicMetadataResolverAttributes?.persistentCacheManagerRef, + persistentCacheManagerDirectory: resolver.dynamicMetadataResolverAttributes?.persistentCacheManagerDirectory, + persistentCacheKeyGeneratorRef: resolver.dynamicMetadataResolverAttributes?.persistentCacheKeyGeneratorRef, + initializeFromPersistentCacheInBackground: !resolver.dynamicMetadataResolverAttributes?.initializeFromPersistentCacheInBackground ?: null, + backgroundInitializationFromCacheDelay: resolver.dynamicMetadataResolverAttributes?.backgroundInitializationFromCacheDelay, + initializationFromCachePredicateRef: resolver.dynamicMetadataResolverAttributes?.initializationFromCachePredicateRef) { + + childNodes() + } + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java new file mode 100644 index 000000000..866f54231 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderController.java @@ -0,0 +1,111 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.repository.DynamicHttpMetadataResolverRepository; +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/DynamicHttp") +public class DynamicHttpMetadataProviderController { + private static final Logger logger = LoggerFactory.getLogger(DynamicHttpMetadataProviderController.class); + + @Autowired + DynamicHttpMetadataResolverRepository 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) { + DynamicHttpMetadataResolver 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) { + DynamicHttpMetadataResolver 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 DynamicHttpMetadataResolver resolver) { + if (repository.findByName(resolver.getName()) != null) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + DynamicHttpMetadataResolver persistedResolver = repository.save(resolver); + persistedResolver.setVersion(persistedResolver.hashCode()); + + return ResponseEntity + .created(getResourceUriFor(persistedResolver)) + .body(persistedResolver); + } + + @PutMapping + public ResponseEntity update(@RequestBody DynamicHttpMetadataResolver resolver) { + DynamicHttpMetadataResolver 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? + + DynamicHttpMetadataResolver updatedResolver = repository.save(resolver); + updatedResolver.setVersion(updatedResolver.hashCode()); + + return ResponseEntity.ok(updatedResolver); + } + + private static URI getResourceUriFor(DynamicHttpMetadataResolver resolver) { + return ServletUriComponentsBuilder + .fromCurrentServletMapping().path("/api/MetadataProvider/DynamicHttp/") + .pathSegment(resolver.getResourceId()) + .build() + .toUri(); + } + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java new file mode 100644 index 000000000..2d3a104ee --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderController.java @@ -0,0 +1,110 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.repository.LocalDynamicMetadataResolverRepository; +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/LocalDynamic") +public class LocalDynamicMetadataProviderController { + private static final Logger logger = LoggerFactory.getLogger(LocalDynamicMetadataProviderController.class); + + @Autowired + LocalDynamicMetadataResolverRepository 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) { + LocalDynamicMetadataResolver 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) { + LocalDynamicMetadataResolver 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 LocalDynamicMetadataResolver resolver) { + if (repository.findByName(resolver.getName()) != null) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + LocalDynamicMetadataResolver persistedResolver = repository.save(resolver); + persistedResolver.setVersion(persistedResolver.hashCode()); + + return ResponseEntity + .created(getResourceUriFor(persistedResolver)) + .body(persistedResolver); + } + + @PutMapping + public ResponseEntity update(@RequestBody LocalDynamicMetadataResolver resolver) { + LocalDynamicMetadataResolver 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? + + LocalDynamicMetadataResolver updatedResolver = repository.save(resolver); + updatedResolver.setVersion(updatedResolver.hashCode()); + + return ResponseEntity.ok(updatedResolver); + } + + private static URI getResourceUriFor(LocalDynamicMetadataResolver resolver) { + return ServletUriComponentsBuilder + .fromCurrentServletMapping().path("/api/MetadataProvider/LocalDynamic/") + .pathSegment(resolver.getResourceId()) + .build() + .toUri(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java index d11598a8d..40de7ea46 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java @@ -131,12 +131,9 @@ public ResponseEntity update(@PathVariable String metadataResolverId, metadataResolverService.reloadFilters(persistedMr.getName()); - int version = updatedFilter.getVersion(); MetadataFilter persistedFilter = convertIntoTransientRepresentationIfNecessary(persistedMr.getMetadataFilters().stream(), updatedFilter.getResourceId()); - int persitedVersion = persistedFilter.getVersion(); - return ResponseEntity.ok().body(persistedFilter); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index 53cd07956..7b267d1ee 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -18,8 +18,7 @@ import javax.persistence.PreUpdate; import javax.persistence.Transient; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; + import java.util.List; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromAttributeReleaseList; @@ -67,13 +66,7 @@ public void fromTransientRepresentation() { attributeList.addAll(getAttributeListFromRelyingPartyOverridesRepresentation(this.relyingPartyOverrides)); if(!attributeList.isEmpty()) { - //attributeList.sort(Comparator.comparing(org.opensaml.saml.saml2.core.Attribute::getName)); this.attributes = (List) (List) attributeList; } - - /*if(!attributeList.isEmpty()) { - this.attributes.clear(); - this.attributes.addAll((List)(List)attributeList); - }*/ } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java new file mode 100644 index 000000000..0910c9050 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java @@ -0,0 +1,47 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.ElementCollection; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.OrderColumn; +import java.util.List; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@Entity +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +@ToString +public class DynamicHttpMetadataResolver extends MetadataResolver { + + public static final String DEFAULT_TIMEOUT = "PT5S"; + + @Embedded + private DynamicMetadataResolverAttributes dynamicMetadataResolverAttributes; + + @Embedded + private HttpMetadataResolverAttributes httpMetadataResolverAttributes; + + private Integer maxConnectionsTotal = 100; + + private Integer maxConnectionsPerRoute = 100; + + @ElementCollection + @OrderColumn + private List supportedContentTypes; + + public DynamicHttpMetadataResolver() { + this.httpMetadataResolverAttributes = new HttpMetadataResolverAttributes(); + this.httpMetadataResolverAttributes.setConnectionRequestTimeout(DEFAULT_TIMEOUT); + this.httpMetadataResolverAttributes.setConnectionTimeout(DEFAULT_TIMEOUT); + this.httpMetadataResolverAttributes.setSocketTimeout(DEFAULT_TIMEOUT); + this.dynamicMetadataResolverAttributes = new DynamicMetadataResolverAttributes(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicMetadataResolverAttributes.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicMetadataResolverAttributes.java new file mode 100644 index 000000000..564f2871e --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicMetadataResolverAttributes.java @@ -0,0 +1,47 @@ +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 DynamicMetadataResolverAttributes { + + private String parserPoolRef; + + private String taskTimerRef; + + private Double refreshDelayFactor = 0.75; + + private String minCacheDuration = "PT10M"; + + private String maxCacheDuration = "PT8H"; + + private String maxIdleEntityData = "PT8H"; + + private Boolean removeIdleEntityData; + + private String cleanupTaskInterval = "PT30M"; + + private String persistentCacheManagerRef; + + private String persistentCacheManagerDirectory; + + private String persistentCacheKeyGeneratorRef; + + private Boolean initializeFromPersistentCacheInBackground = true; + + private String backgroundInitializationFromCacheDelay = "PT2S"; + + private String initializationFromCachePredicateRef; + +} 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 index 706c34e08..8054ba852 100644 --- 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 @@ -28,7 +28,7 @@ public class HttpMetadataResolverAttributes { private String socketTimeout; - private Boolean disregardTLSCertificate; + private Boolean disregardTLSCertificate = false; private String tlsTrustEngineRef; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java new file mode 100644 index 000000000..63ec78eb5 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java @@ -0,0 +1,29 @@ +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 LocalDynamicMetadataResolver extends MetadataResolver { + + private String sourceDirectory; + + private String sourceManagerRef; + + private String sourceKeyGeneratorRef; + + @Embedded + private DynamicMetadataResolverAttributes dynamicMetadataResolverAttributes; + +} 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 index 659380db6..1bbafefba 100644 --- 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 @@ -45,7 +45,7 @@ public class MetadataResolver extends AbstractAuditable { private Boolean useDefaultPredicateRegistry = true; - private Boolean satisfyAnyPredicates; + private Boolean satisfyAnyPredicates = false; @OneToMany(cascade = CascadeType.ALL) @OrderColumn diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/DynamicHttpMetadataResolverRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/DynamicHttpMetadataResolverRepository.java new file mode 100644 index 000000000..c4a08804f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/DynamicHttpMetadataResolverRepository.java @@ -0,0 +1,18 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver; +import org.springframework.data.repository.CrudRepository; + +/** + * Spring Data CRUD repository for instances of {@link DynamicHttpMetadataResolver}s. + * + * @author Bill Smith (wsmith@unicon.net) + */ +public interface DynamicHttpMetadataResolverRepository extends CrudRepository { + + DynamicHttpMetadataResolver findByName(String name); + + boolean deleteByResourceId(String resourceId); + + DynamicHttpMetadataResolver findByResourceId(String resourceId); +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepository.java new file mode 100644 index 000000000..323b470d5 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepository.java @@ -0,0 +1,16 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver; +import org.springframework.data.repository.CrudRepository; + +/** + * Spring Data CRUD repository for instances of {@link LocalDynamicMetadataResolver}s. + */ +public interface LocalDynamicMetadataResolverRepository extends CrudRepository { + + LocalDynamicMetadataResolver findByName(String name); + + boolean deleteByResourceId(String resourceId); + + LocalDynamicMetadataResolver findByResourceId(String resourceId); +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy new file mode 100644 index 000000000..7296a2063 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -0,0 +1,69 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration + +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import net.shibboleth.ext.spring.resource.ResourceHelper +import net.shibboleth.utilities.java.support.component.ComponentInitializationException +import org.apache.lucene.document.Document +import org.apache.lucene.document.Field +import org.apache.lucene.document.StringField +import org.apache.lucene.document.TextField +import org.apache.lucene.index.IndexWriter +import org.opensaml.saml.metadata.resolver.ChainingMetadataResolver +import org.opensaml.saml.metadata.resolver.MetadataResolver +import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.io.ClassPathResource + +@Configuration +class TestConfiguration { + final OpenSamlObjects openSamlObjects + final IndexWriter indexWriter + final MetadataResolverRepository metadataResolverRepository + + TestConfiguration(final OpenSamlObjects openSamlObjects, final IndexWriter indexWriter, final MetadataResolverRepository metadataResolverRepository) { + this.openSamlObjects =openSamlObjects + this.indexWriter = indexWriter + this.metadataResolverRepository = metadataResolverRepository + } + + @Bean + MetadataResolver metadataResolver() { + ChainingMetadataResolver metadataResolver = new ChainingMetadataResolver() + metadataResolver.setId("chain") + + def shortIncommon = new ResourceBackedMetadataResolver(ResourceHelper.of(new ClassPathResource('/metadata/incommon-short.xml'))){ + @Override + protected void initMetadataResolver() throws ComponentInitializationException { + super.initMetadataResolver() + + for (String entityId: this.getBackingStore().getIndexedDescriptors().keySet()) { + Document document = new Document(); + document.add(new StringField("id", entityId, Field.Store.YES)); + document.add(new TextField("content", entityId, Field.Store.YES)); // TODO: change entityId to be content of entity descriptor block + try { + indexWriter.addDocument(document); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + try { + indexWriter.commit() + } catch (IOException e) { + throw new ComponentInitializationException(e) + } + } + }.with { + it.id = 'test' + TestConfiguration p = owner + it.parserPool = p.openSamlObjects.parserPool + it.initialize() + it + } + + metadataResolver.resolvers = [shortIncommon] + metadataResolver.initialize() + return metadataResolver + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy new file mode 100644 index 000000000..bf4ce2ffe --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DynamicHttpMetadataProviderControllerTests.groovy @@ -0,0 +1,271 @@ +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.repository.DynamicHttpMetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +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.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 DynamicHttpMetadataProviderControllerTests extends Specification { + RandomGenerator randomGenerator + TestObjectGenerator testObjectGenerator + ObjectMapper mapper + + def repository = Mock(DynamicHttpMetadataResolverRepository) + def controller + def mockMvc + + @Autowired + AttributeUtility attributeUtility + + def setup() { + randomGenerator = new RandomGenerator() + testObjectGenerator = new TestObjectGenerator(attributeUtility) + mapper = new ObjectMapper() + + controller = new DynamicHttpMetadataProviderController ( + 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/DynamicHttp/$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/DynamicHttp/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "POST a new resolver properly persists and returns the new persisted resolver"() { + given: + def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + resolver.setVersion(resolver.hashCode()) + def postedJsonBody = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolver.getName()) >> null + 1 * repository.save(_) >> resolver + + def expectedResolverUUID = resolver.getResourceId() + def expectedResponseHeader = 'Location' + def expectedResponseHeaderValue = "/api/MetadataProvider/DynamicHttp/$expectedResolverUUID" + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/DynamicHttp') + .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 resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + def resolverName = resolver.name + def postedJsonBody = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolverName) >> resolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/DynamicHttp') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isConflict()) + } + + def "GET by resourceId returns the desired persisted resolver"() { + given: + def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + resolver.version = resolver.hashCode() + def resourceId = resolver.resourceId + def resolverJson = mapper.writeValueAsString(resolver) + + 1 * repository.findByResourceId(resourceId) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/DynamicHttp/$resourceId")) + + 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/DynamicHttp/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "GET by resolver name returns the desired persisted resolver"() { + given: + def resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + resolver.version = resolver.hashCode() + def resolverName = resolver.name + def resolverJson = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolverName) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/DynamicHttp/name/$resolverName")) + + 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/DynamicHttp/name/$randomResolverName")) + + then: + result.andExpect(status().isNotFound()) + } + + def "PUT allows for a successful update of an already-persisted resolver"() { + given: + def existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + existingResolver.version = existingResolver.hashCode() + def resourceId = existingResolver.resourceId + def existingResolverJson = mapper.writeValueAsString(existingResolver) + + def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + updatedResolver.resourceId = existingResolver.resourceId + updatedResolver.version = existingResolver.version + def postedJsonBody = mapper.writeValueAsString(updatedResolver) + + 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver + 1 * repository.save(_) >> updatedResolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/DynamicHttp') + .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 existingResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + existingResolver.version = existingResolver.hashCode() + + def updatedResolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + updatedResolver.resourceId = existingResolver.resourceId + updatedResolver.version = updatedResolver.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedResolver) + + 1 * repository.findByResourceId(existingResolver.resourceId) >> existingResolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/DynamicHttp') + .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 resolver = testObjectGenerator.buildDynamicHttpMetadataResolver() + def postedJsonBody = mapper.writeValueAsString(resolver) + + 1 * repository.findByResourceId(resolver.resourceId) >> null + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/DynamicHttp') + .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/FileBackedHttpMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FileBackedHttpMetadataProviderControllerTests.groovy index 19975e843..99457a335 100644 --- 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 @@ -1,10 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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 edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator @@ -28,7 +27,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class FileBackedHttpMetadataProviderControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy new file mode 100644 index 000000000..bc13df607 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/LocalDynamicMetadataProviderControllerTests.groovy @@ -0,0 +1,275 @@ +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.repository.LocalDynamicMetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +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.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 LocalDynamicMetadataProviderControllerTests extends Specification { + RandomGenerator randomGenerator + TestObjectGenerator testObjectGenerator + ObjectMapper mapper + + def repository = Mock(LocalDynamicMetadataResolverRepository) + def controller + def mockMvc + + @Autowired + AttributeUtility attributeUtility + + def setup() { + randomGenerator = new RandomGenerator() + testObjectGenerator = new TestObjectGenerator(attributeUtility) + mapper = new ObjectMapper() + + controller = new LocalDynamicMetadataProviderController ( + 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/LocalDynamic/$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/LocalDynamic/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "POST a new resolver properly persists and returns the new persisted resolver"() { + given: + def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + resolver.version = resolver.hashCode() + def postedJsonBody = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolver.getName()) >> null + 1 * repository.save(_) >> resolver + + def expectedResolverUUID = resolver.getResourceId() + def expectedResponseHeader = 'Location' + def expectedResponseHeaderValue = "/api/MetadataProvider/LocalDynamic/$expectedResolverUUID" + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/LocalDynamic') + .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 resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + resolver.version = resolver.hashCode() + def postedJsonBody = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolver.name) >> resolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + post('/api/MetadataProvider/LocalDynamic') + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().isConflict()) + } + + def "GET by resourceId returns the desired persisted resolver"() { + given: + def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + resolver.version = resolver.hashCode() + def resolverJson = mapper.writeValueAsString(resolver) + def resolverId = resolver.resourceId + + 1 * repository.findByResourceId(resolverId) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/LocalDynamic/$resolverId")) + + 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/LocalDynamic/$randomResourceId")) + + then: + result.andExpect(status().isNotFound()) + } + + def "GET by resolver name returns the desired persisted resolver"() { + given: + def resolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + resolver.version = resolver.hashCode() + def resolverName = resolver.name + def resolverJson = mapper.writeValueAsString(resolver) + + 1 * repository.findByName(resolverName) >> resolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + get("/api/MetadataProvider/LocalDynamic/name/$resolverName")) + + 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/LocalDynamic/name/$randomResolverName")) + + then: + result.andExpect(status().isNotFound()) + } + + def "PUT allows for a successful update of an already-persisted resolver"() { + given: + def existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + existingResolver.version = existingResolver.hashCode() + def resourceId = existingResolver.resourceId + def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + updatedResolver.setResourceId(resourceId) + updatedResolver.setVersion(existingResolver.hashCode()) + def postedJsonBody = mapper.writeValueAsString(updatedResolver) + + 1 * repository.findByResourceId(resourceId) >> existingResolver + 1 * repository.save(_) >> updatedResolver + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/LocalDynamic') + .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 existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + existingResolver.version = existingResolver.hashCode() + def resourceId = existingResolver.resourceId + + def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + updatedResolver.resourceId = resourceId + updatedResolver.version = updatedResolver.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedResolver) + + 1 * repository.findByResourceId(resourceId) >> existingResolver + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/LocalDynamic') + .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 existingResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + existingResolver.version = existingResolver.hashCode() + def resourceId = existingResolver.resourceId + + def updatedResolver = testObjectGenerator.buildLocalDynamicMetadataResolver() + updatedResolver.resourceId = resourceId + updatedResolver.version = updatedResolver.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedResolver) + + 1 * repository.findByResourceId(resourceId) >> null + 0 * repository.save(_) + + when: + def result = mockMvc.perform( + put('/api/MetadataProvider/LocalDynamic') + .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/MetadataFiltersControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy index f2351dfe8..53cfb6a01 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy @@ -2,8 +2,8 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter @@ -35,7 +35,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class MetadataFiltersControllerTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy index 9237228e2..8935e2eac 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EnityDescriptorRepositoryTest.groovy @@ -1,7 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.repository +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.EntityDescriptor import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects @@ -21,7 +21,7 @@ import javax.persistence.EntityManager * A highly unnecessary test so that I can check to make sure that persistence is correct for the model */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD) 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 index 8f38882d5..619b650f9 100644 --- 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 @@ -1,8 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.repository import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget @@ -23,7 +23,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttrib import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes.HttpCachingType.memory @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD) @@ -54,7 +54,7 @@ class FileBackedHttpMetadataResolverRepositoryTests extends Specification { it.metadataFilters.add(new EntityAttributesFilter().with { it.entityAttributesFilterTarget = new EntityAttributesFilterTarget().with { it.entityAttributesFilterTargetType = ENTITY - it.setSingleValue(["hola"]) + it.setValue(["hola"]) it } it diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy new file mode 100644 index 000000000..027b9c030 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/LocalDynamicMetadataResolverRepositoryTests.groovy @@ -0,0 +1,79 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository + +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.filters.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicMetadataResolverAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver +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.filters.EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY + +@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 LocalDynamicMetadataResolverRepositoryTests extends Specification { + + @Autowired + LocalDynamicMetadataResolverRepository repositoryUnderTest + + @Autowired + EntityManager entityManager + + def "local dynamic metadata resolver instances persist OK"() { + when: + def mdr = new LocalDynamicMetadataResolver().with { + it.name = 'LocalDynamicMetadataResolver' + it.sourceDirectory = '/etc/shibui' + it.sourceKeyGeneratorRef = 'sourceKeyGeneratorBean' + it.sourceManagerRef = 'sourceManagerBean' + it.dynamicMetadataResolverAttributes = new DynamicMetadataResolverAttributes().with { + it.cleanupTaskInterval = 'PT5H' + it.maxCacheDuration = 'PT8H' + it.initializeFromPersistentCacheInBackground = true + 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 + def item = repositoryUnderTest.findByName("LocalDynamicMetadataResolver") + + item.name == "LocalDynamicMetadataResolver" + 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.sourceDirectory == '/etc/shibui' + item.sourceKeyGeneratorRef == 'sourceKeyGeneratorBean' + item.sourceManagerRef == 'sourceManagerBean' + item.dynamicMetadataResolverAttributes.cleanupTaskInterval == 'PT5H' + item.dynamicMetadataResolverAttributes.maxCacheDuration == 'PT8H' + item.dynamicMetadataResolverAttributes.initializeFromPersistentCacheInBackground + } + +} 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 4ec3b06ed..cf87e218c 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 @@ -1,9 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.repository +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation @@ -11,7 +10,6 @@ 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 - import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -26,7 +24,7 @@ import javax.persistence.EntityManager * A highly unnecessary test so that I can check to make sure that persistence is correct for the model */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy index a3b8fdf7c..73eb2c58c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy @@ -1,7 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.scheduled +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.frontend.EntityDescriptorRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation @@ -23,7 +23,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityDescriptorFilesScheduledTasksTests extends Specification { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy index 027f7e52c..55777e6ec 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy @@ -1,7 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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 org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan @@ -14,7 +14,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class EntityIdsSearchServiceTests extends Specification { 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 7bf8c2cba..6a90b2507 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 @@ -19,6 +19,8 @@ import org.springframework.context.annotation.Bean import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration +import org.xmlunit.builder.DiffBuilder +import org.xmlunit.builder.Input import spock.lang.Specification import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.* @@ -43,8 +45,10 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { when: def output = metadataResolverService.generateConfiguration() + println(output.documentElement) + then: - assert generatedXmlIsTheSameAsExpectedXml('/conf/278.xml', output) + generatedXmlIsTheSameAsExpectedXml('/conf/278.xml', output) } def 'test generation of metadata-providers.xml with filters'() { @@ -71,7 +75,7 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { def output = metadataResolverService.generateConfiguration() then: - assert generatedXmlIsTheSameAsExpectedXml('/conf/278.2.xml', output) + generatedXmlIsTheSameAsExpectedXml('/conf/278.2.xml', output) } //TODO: check that this configuration is sufficient @@ -106,6 +110,12 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { def mr = new TestObjectGenerator(attributeUtility).fileBackedHttpMetadataResolver() mr.setName("HTTPMetadata") metadataResolverRepository.save(mr) + + // Generate and test edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver. + metadataResolverRepository.save(new TestObjectGenerator(attributeUtility).dynamicHttpMetadataResolver()) + + // Generate and test edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver. + metadataResolverRepository.save(new TestObjectGenerator(attributeUtility).localDynamicMetadataResolver()) } return resolver diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy index 8e212bb90..607ed24cc 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy @@ -1,11 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.XSString import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers @@ -23,7 +21,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class JPAEntityServiceImplTests extends Specification { @@ -32,7 +30,7 @@ class JPAEntityServiceImplTests extends Specification { OpenSamlObjects openSamlObjects @Autowired - AttributeUtility attributeUtility; + AttributeUtility attributeUtility def randomGenerator def testObjectGenerator diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy index 828242646..11ceb346f 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy @@ -1,7 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.service +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration 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.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers @@ -18,7 +18,7 @@ import spock.lang.Specification * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") class JPAFilterServiceImplTests extends Specification { 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 c3dadb8c2..546315d6f 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 @@ -138,7 +138,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { genXmlSnippet(markupBuilder) { JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) } then: - assert generatedXmlIsTheSameAsExpectedXml('/conf/533.xml', domBuilder.parseText(writer.toString())) + generatedXmlIsTheSameAsExpectedXml('/conf/533.xml', domBuilder.parseText(writer.toString())) } def 'test generating FileBackedHttMetadataResolver xml snippet'() { @@ -151,7 +151,7 @@ class JPAMetadataResolverServiceImplTests extends Specification { } then: - assert generatedXmlIsTheSameAsExpectedXml('/conf/532.xml', domBuilder.parseText(writer.toString())) + generatedXmlIsTheSameAsExpectedXml('/conf/532.xml', domBuilder.parseText(writer.toString())) } static genXmlSnippet(MarkupBuilder xml, Closure xmlNodeGenerator) { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy index 0212a788f..c2c6f7761 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy @@ -26,8 +26,9 @@ class TestHelpers { return count } - static generatedXmlIsTheSameAsExpectedXml(String expectedXmlResource, Document generatedXml) { - !DiffBuilder.compare(Input.fromStream(TestHelpers.getResourceAsStream(expectedXmlResource))).withTest(Input.fromDocument(generatedXml)) + static void generatedXmlIsTheSameAsExpectedXml(String expectedXmlResource, Document generatedXml) { + assert !DiffBuilder.compare(Input.fromStream(TestHelpers.getResourceAsStream(expectedXmlResource))).withTest(Input.fromDocument(generatedXml)) .ignoreComments().ignoreWhitespace().build().hasDifferences() + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy index 1b020c499..4069ab849 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -1,22 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.util -import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute -import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson +import edu.internet2.tier.shibboleth.admin.ui.domain.* import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget -import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor -import edu.internet2.tier.shibboleth.admin.ui.domain.LocalizedName -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.filters.EntityRoleWhiteListFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterTargetRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation -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 edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.* import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import edu.internet2.tier.shibboleth.admin.util.MDDCConstants import org.opensaml.saml.saml2.metadata.Organization @@ -36,6 +28,25 @@ class TestObjectGenerator { this.attributeUtility = attributeUtility } + DynamicHttpMetadataResolver buildDynamicHttpMetadataResolver() { + def resolver = new DynamicHttpMetadataResolver().with { + it.dynamicMetadataResolverAttributes = buildDynamicMetadataResolverAttributes() + it.httpMetadataResolverAttributes = buildHttpMetadataResolverAttributes() + it.maxConnectionsPerRoute = generator.randomInt(1, 100) + it.maxConnectionsTotal = generator.randomInt(1, 100) + it.supportedContentTypes = generator.randomStringList() + it.name = generator.randomString(10) + it.requireValidMetadata = generator.randomBoolean() + it.failFastInitialization = generator.randomBoolean() + it.sortKey = generator.randomInt(1, 10) + it.criterionPredicateRegistryRef = generator.randomString(10) + it.useDefaultPredicateRegistry = generator.randomBoolean() + it.satisfyAnyPredicates = generator.randomBoolean() + it.metadataFilters = buildAllTypesOfFilterList() + it + } + return resolver + } HttpMetadataResolverAttributes buildHttpMetadataResolverAttributes() { def attributes = new HttpMetadataResolverAttributes().with { @@ -59,6 +70,50 @@ class TestObjectGenerator { return attributes } + HttpMetadataResolverAttributes.HttpCachingType randomHttpCachingType() { + HttpMetadataResolverAttributes.HttpCachingType.values()[generator.randomInt(0, 2)] + } + + LocalDynamicMetadataResolver buildLocalDynamicMetadataResolver() { + def resolver = new LocalDynamicMetadataResolver().with { + it.dynamicMetadataResolverAttributes = buildDynamicMetadataResolverAttributes() + it.sourceDirectory = generator.randomString(10) + it.sourceKeyGeneratorRef = generator.randomString(10) + it.sourceManagerRef = generator.randomString(10) + it.failFastInitialization = generator.randomBoolean() + it.name = generator.randomString(10) + it.requireValidMetadata = generator.randomBoolean() + it.useDefaultPredicateRegistry = generator.randomBoolean() + it.criterionPredicateRegistryRef = generator.randomString(10) + it.satisfyAnyPredicates = generator.randomBoolean() + it.sortKey = generator.randomInt(1, 10) + it.metadataFilters = buildAllTypesOfFilterList() + it + } + return resolver + } + + DynamicMetadataResolverAttributes buildDynamicMetadataResolverAttributes() { + def attributes = new DynamicMetadataResolverAttributes().with { + it.backgroundInitializationFromCacheDelay = generator.randomString(10) + it.cleanupTaskInterval = generator.randomString(10) + it.initializationFromCachePredicateRef = generator.randomString(10) + it.initializeFromPersistentCacheInBackground = generator.randomBoolean() + it.maxCacheDuration = generator.randomString(5) + it.maxIdleEntityData = generator.randomString(5) + it.minCacheDuration = generator.randomString(5) + it.parserPoolRef = generator.randomString(10) + it.persistentCacheKeyGeneratorRef = generator.randomString(10) + it.persistentCacheManagerDirectory = generator.randomString(10) + it.persistentCacheManagerRef = generator.randomString(10) + it.refreshDelayFactor = generator.randomInt(1, 5) + it.removeIdleEntityData = generator.randomBoolean() + it.taskTimerRef = generator.randomString() + it + } + attributes + } + List buildAllTypesOfFilterList() { List filterList = new ArrayList<>() (1..generator.randomInt(4, 10)).each { @@ -262,6 +317,20 @@ class TestObjectGenerator { } } + DynamicHttpMetadataResolver dynamicHttpMetadataResolver() { + new DynamicHttpMetadataResolver().with { + it.name = 'DynamicHTTP' + it + } + } + + LocalDynamicMetadataResolver localDynamicMetadataResolver() { + new LocalDynamicMetadataResolver().with { + it.name = 'LocalDynamic' + it + } + } + FileBackedHttpMetadataResolver buildFileBackedHttpMetadataResolver() { def resolver = new FileBackedHttpMetadataResolver() resolver.name = generator.randomString(10) @@ -290,10 +359,6 @@ class TestObjectGenerator { return attributes } - HttpMetadataResolverAttributes.HttpCachingType randomHttpCachingType() { - HttpMetadataResolverAttributes.HttpCachingType.values()[generator.randomInt(0, 2)] - } - /** * This method takes a type and a size and builds a List of that size containing objects of that type. This is * intended to be used with things that extend LocalizedName such as {@link OrganizationName}, {@link OrganizationDisplayName}, diff --git a/backend/src/test/resources/conf/278.2.xml b/backend/src/test/resources/conf/278.2.xml index 7b8718caf..b50d8e3d7 100644 --- a/backend/src/test/resources/conf/278.2.xml +++ b/backend/src/test/resources/conf/278.2.xml @@ -8,6 +8,31 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" 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"> + + + + + + there + + https://sp1.example.org + + - - - there - - https://sp1.example.org - + + + + diff --git a/backend/src/test/resources/conf/278.xml b/backend/src/test/resources/conf/278.xml index 5d02714ff..be69c5487 100644 --- a/backend/src/test/resources/conf/278.xml +++ b/backend/src/test/resources/conf/278.xml @@ -8,6 +8,24 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" 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"> + + + + - + + + + diff --git a/backend/src/test/resources/metadata/incommon-short.xml b/backend/src/test/resources/metadata/incommon-short.xml new file mode 100644 index 000000000..c807cf343 --- /dev/null +++ b/backend/src/test/resources/metadata/incommon-short.xml @@ -0,0 +1,246 @@ + + + + + + + + http://id.incommon.org/category/research-and-scholarship + http://refeds.org/category/research-and-scholarship + http://id.incommon.org/category/registered-by-incommon + + + + + + + + + + + CarmenWiki + Enterprise Wiki Service at the Ohio State University. + https://ocio.osu.edu/services/view/carmenwiki-wiki-services + https://carmenwiki.osu.edu/x/jyLeAQ + https://carmenwiki.osu.edu/download/attachments/9666561/global.logo + + + + + + + + MIIDGzCCAgOgAwIBAgIJANI+yGM0M1N2MA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNV + BAMTHGx0Y2F3aWtpMDEuaXQub2hpby1zdGF0ZS5lZHUwHhcNMTAwNzA3MjI0MzA1 + WhcNMjAwNzA0MjI0MzA1WjAnMSUwIwYDVQQDExxsdGNhd2lraTAxLml0Lm9oaW8t + c3RhdGUuZWR1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5fsEv25M + r9wfa48qfjn8m40yB/lwimJ8dSnYw2erd/tfB+sPESw42Is5Lv2B3pI3mj9a0PT0 + Gf1VgUoQW0RCT6L4VOW50WsPFv/RKPfT/AIRl00dTCqb440PgotGbrK9ivZqlvkz + lSGUKuFcg2gLj+CJlbMcwEneSwn0FE1xKEGpMDUk91lZH1XxmnIDDOQn1G5qul4q + AbXITMpLi2MlsHAEXxnLrthFFas6zDrviTwHcqGXq9zJJkPHDcbu1qg6AUT7bRJr + qszxxktSV6mFclkgLPpcVkigMR8RNVMQkWaaWSnfBkFy2iAe3xw3DNp7obtzgItY + i9N8U6K5qorSkQIDAQABo0owSDAnBgNVHREEIDAeghxsdGNhd2lraTAxLml0Lm9o + aW8tc3RhdGUuZWR1MB0GA1UdDgQWBBR32XnCliG78DdyTtZhyIQSHChtyjANBgkq + hkiG9w0BAQUFAAOCAQEAVEweCxPElHGmam4Iv2QeJsGE7m4de7axp3epAJb7uVbN + Z2P1S/s4GZQhmGsUoGoxwqca3wyQ+C1ZkpQJdyFl5s1tFc26D+Z0KTDo174GzO9i + I9SeQ4YSp3FNhZqxn4xH3DULzzHwoVSwFr5irLPAVtrqK8H/rzBREhqOse2VSJ/1 + PkI+p7lUiElIzMiObLGjumF2fDOPkXOSMNyC4c5oCCJtcrip/BaLo6bqdqn3DKP8 + onMw/lHZQolyVsupuhGsSX13WVJ0uyGvuA7hiHnGEkpDmskUd3TsriyQAt47RZzY + tTupO/NdWvz8SvXU1qIOk9CTQ0D2b2OOftfUW+FuAQ== + + + + + + + + + + + + + + + + CarmenWiki + Enterprise Wiki Service at the Ohio State University. + + + + + + + The Ohio State University + Ohio State University + http://www.osu.edu/ + + + Travis Ritter + ritter.18@osu.edu + + + IT Service Desk + 8help@osu.edu + + + Web Authentication Support + webauth-admin@lists.service.ohio-state.edu + + + Web Authentication Support + webauth-admin@lists.service.ohio-state.edu + + + + + + + + http://id.incommon.org/category/registered-by-incommon + + + + + + unicon.net + + Unicon, Inc. + Login service for Unicon Employees + https://www.unicon.net/files/Image/unicon_logo_600_high.jpg + + + + + + + + MIIDIzCCAgugAwIBAgIUIEHTfbStY0ckKZzxIgqd5p1O2K0wDQYJKoZIhvcNAQEF + BQAwGTEXMBUGA1UEAxMOaWRwLnVuaWNvbi5uZXQwHhcNMTEwOTEzMDMyMzE2WhcN + MzEwOTEzMDMyMzE2WjAZMRcwFQYDVQQDEw5pZHAudW5pY29uLm5ldDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBANtUsFXxlhvD3bWT5Y7TqKkf5rxa+dPA + z7vpbJ6bWhDPSMXb/9MiJe/ciY5ZKKrB1rdRC04s7blrzem3YtjGihfGd4ld+NRt + Pi0xoAT2YIp83CvEe5BHAKwqD7KTonN1unbN84mVo65itbme9d8lZKc0PfLM+BQp + fhXKUBfYeBCkYU4YWxmgL4Vs7XBaKjEjpTN4ncar4YSrarWTTPyO5RzmVPLAcv88 + 1OBqewTyN41+JRXt0Jopi4ZQ8JjKkm73vhoYDBPHr/VMqk1lFfrDcDwJa2ygyWCm + qTlq6zyLE9Fr6sYz6CbgA2lAqu/b1rYCqVCnRpoHZKahAQ9uGQSfHD8CAwEAAaNj + MGEwQAYDVR0RBDkwN4IOaWRwLnVuaWNvbi5uZXSGJWh0dHBzOi8vaWRwLnVuaWNv + bi5uZXQvaWRwL3NoaWJib2xldGgwHQYDVR0OBBYEFK6yUrpGjvY3B09ke0kVl4wA + CMAnMA0GCSqGSIb3DQEBBQUAA4IBAQDG/gMpr3N+nAMuo7RhtDBsckiJV2+BwT/r + JmpxlHAV1Zgc3eeuOdyxm5/jA78tspLldL0+6W/LzZWov/je36IqVT1wSGy1n0Sc + Pjw8DHgyEJLCij2vVScV+j/Y4Eg0bVy6pZTeQW+e3ygb6WgiVT/ARM8QBp6GjAUC + qIlJCads9Rcx3vAih72I4exUUD4qMuBMeLIdY5XReHy5YHqxbkPjQhDIEORAFlzJ + jLqO/Ldzn4waEa5snDZyeYjsl6pi+8CVGfXLSDVsDuk5s47B9OD+gOSJ1wEc7O/N + nU9d/WCcM1V4IGZGL8TXUdfJoVXYZUFF08jUGSL2mj30WS1orIWo + + + + + + + + + + + Unicon, Inc. + Unicon, Inc. + http://www.unicon.net/ + + + IdP Administrator + idp-admin@unicon.net + + + John Blakley + blakley@unicon.net + + + Security Operations + sec-ops@unicon.net + + + + + + + + http://id.incommon.org/category/registered-by-incommon + + + + + + + Canvas - Kevin Test + Canvas - Kevin Test + https://canvaslms.com + https://www.canvaslms.com/policies/privacy + https://www.canvaslms.com/img/logo/instructure.png + + + + + + + + MIIEKzCCAxOgAwIBAgIJAPuxHXVDuOfZMA0GCSqGSIb3DQEBCwUAMIGrMQswCQYD + VQQGEwJVUzENMAsGA1UECAwEVXRhaDEXMBUGA1UEBwwOU2FsdCBMYWtlIENpdHkx + GTAXBgNVBAoMEEluc3RydWN0dXJlLCBJbmMxEzARBgNVBAsMCk9wZXJhdGlvbnMx + IDAeBgNVBAMMF0NhbnZhcyBTQU1MIENlcnRpZmljYXRlMSIwIAYJKoZIhvcNAQkB + FhNvcHNAaW5zdHJ1Y3R1cmUuY29tMB4XDTE3MDQwODIxMDQwNVoXDTE5MDQyMjIx + MDQwNVowgasxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMRcwFQYDVQQHDA5T + YWx0IExha2UgQ2l0eTEZMBcGA1UECgwQSW5zdHJ1Y3R1cmUsIEluYzETMBEGA1UE + CwwKT3BlcmF0aW9uczEgMB4GA1UEAwwXQ2FudmFzIFNBTUwgQ2VydGlmaWNhdGUx + IjAgBgkqhkiG9w0BCQEWE29wc0BpbnN0cnVjdHVyZS5jb20wggEiMA0GCSqGSIb3 + DQEBAQUAA4IBDwAwggEKAoIBAQDOokl8TPWm4LL6rqEnPjL0t5QWw76WOTA9JzLJ + xjKwtIWlGAlyRQ+gEmD5vaFAzoYl62BIm2yCy+EdUR9D/3X9Hq+22ysy7pWj8rda + JeQ1XAX2xMlphZhMHnKdKBfDxLMaIEKQg942xBTkY3yeDsc8YezR0sSBToumQs23 + PVnIq7u1U+UAry0Q33ovmJRV50kQk3Qccl6omSDXezUIB+LYqI2ghoIo/+XfHaPe + pHWj3XnXKBGtlDIpUbgVIbqdHcsE9uRHK6YfeiWqX+fW0h8rcn5z3cj5awzYJ8kk + GKACS6TNccfFCouMoKskBB4ot2WBuIKEyJNqg7kn/wlCzsoHAgMBAAGjUDBOMB0G + A1UdDgQWBBQ3lOrDsVPyDFZyZhjbbLZpdStYODAfBgNVHSMEGDAWgBQ3lOrDsVPy + DFZyZhjbbLZpdStYODAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBy + mkrTdaGCS5wSswAKwHmrTdZQdD4ksXszvQKZEiLtenoDyE7JomCME1a5BaGuDjaD + BbVEO8StAEjQeCoGCkW0tkqBfgwSNGaIZp7SXcMSHN866D1r7whwjAXSehVSf1LS + XYyMh2wcgbKVZB71EP8hIG37fl5dcCZJ+qhnExTf+EXgf3MwuE3eTnuiXem2F0lz + 1Vj8vkef+qr9wdSHoZ/L9Xje03wYSAT0J2KlbkUZNb/me6ZxeOWMWbCsYu4+OBfc + Wi+n2KDSFd6xI4DLm0685DP4hzlycGDiUmOHrt/ZpZEVBs1d/ooxfZhs6dCEUyUn + SQJDMGy5cRRcanOu2OwE + + + + + + + + + + + + Canvas - Kevin Test + Canvas - Kevin Test + + + + + Instructure, Inc + Instructure, Inc + http://www.instructure.com/ + + + Canvas Support + support@instructure.com + + + Canvas Support + support@instructure.com + + + Canvas Support + support@instructure.com + + + Canvas Support + support@instructure.com + + + \ No newline at end of file diff --git a/ui/src/app/metadata/domain/component/forms/finish-form.component.ts b/ui/src/app/metadata/domain/component/forms/finish-form.component.ts index 8cd8cac21..f7a3a80df 100644 --- a/ui/src/app/metadata/domain/component/forms/finish-form.component.ts +++ b/ui/src/app/metadata/domain/component/forms/finish-form.component.ts @@ -39,4 +39,4 @@ export class FinishFormComponent extends ProviderFormFragmentComponent implement serviceEnabled: !this.resolver ? false : this.resolver.serviceEnabled !== false ? true : false }); } -} /* istanbul ignore next */ +} diff --git a/ui/src/app/metadata/domain/domain.module.ts b/ui/src/app/metadata/domain/domain.module.ts index 81750f7a5..3349342d9 100644 --- a/ui/src/app/metadata/domain/domain.module.ts +++ b/ui/src/app/metadata/domain/domain.module.ts @@ -13,11 +13,12 @@ import { MetadataProviderService } from './service/provider.service'; import { EntityEffects } from './effect/entity.effect'; import { PreviewDialogComponent } from './component/preview-dialog.component'; -export const COMPONENTS = []; +export const COMPONENTS = [ + PreviewDialogComponent +]; export const DECLARATIONS = [ - ...COMPONENTS, - PreviewDialogComponent + ...COMPONENTS ]; @NgModule({ diff --git a/ui/src/app/metadata/domain/entity/filter/entity-attributes-filter.ts b/ui/src/app/metadata/domain/entity/filter/entity-attributes-filter.ts index a6f7beaa0..82b5ae9d6 100644 --- a/ui/src/app/metadata/domain/entity/filter/entity-attributes-filter.ts +++ b/ui/src/app/metadata/domain/entity/filter/entity-attributes-filter.ts @@ -33,6 +33,10 @@ export class EntityAttributesFilter implements MetadataFilter, MetadataEntity { return this.entityId; } + getDisplayId(): string { + return this.entityId; + } + isDraft(): boolean { return false; } diff --git a/ui/src/app/metadata/domain/entity/provider/file-backed-http-metadata-provider.ts b/ui/src/app/metadata/domain/entity/provider/file-backed-http-metadata-provider.ts index 477fa4278..41898afa5 100644 --- a/ui/src/app/metadata/domain/entity/provider/file-backed-http-metadata-provider.ts +++ b/ui/src/app/metadata/domain/entity/provider/file-backed-http-metadata-provider.ts @@ -56,6 +56,10 @@ export class FileBackedHttpMetadataProvider implements MetadataProvider, Metadat return this.id; } + getDisplayId(): string { + return this.id; + } + isDraft(): boolean { return false; } diff --git a/ui/src/app/metadata/domain/entity/resolver/file-backed-http-metadata-resolver.ts b/ui/src/app/metadata/domain/entity/resolver/file-backed-http-metadata-resolver.ts index 1322cb5d3..982d86d35 100644 --- a/ui/src/app/metadata/domain/entity/resolver/file-backed-http-metadata-resolver.ts +++ b/ui/src/app/metadata/domain/entity/resolver/file-backed-http-metadata-resolver.ts @@ -37,7 +37,7 @@ export class FileBackedHttpMetadataResolver implements MetadataResolver, Metadat nameIdFormats: [] } as IdpSsoDescriptor; - logoutEndpoints = [] as LogoutEndpoint[]; + logoutEndpoints: LogoutEndpoint[] = []; serviceEnabled = false; @@ -56,6 +56,10 @@ export class FileBackedHttpMetadataResolver implements MetadataResolver, Metadat return this.id ? this.id : this.entityId; } + getDisplayId(): string { + return this.entityId; + } + isDraft(): boolean { return this.id ? false : true; } diff --git a/ui/src/app/metadata/domain/model/metadata-entity.ts b/ui/src/app/metadata/domain/model/metadata-entity.ts index a3c7328f4..49d34a78a 100644 --- a/ui/src/app/metadata/domain/model/metadata-entity.ts +++ b/ui/src/app/metadata/domain/model/metadata-entity.ts @@ -4,6 +4,7 @@ export interface MetadataEntity { kind: string; getId(): string; + getDisplayId(): string; isDraft(): boolean; getCreationDate(): Date; diff --git a/ui/src/app/metadata/manager/component/entity-item.component.html b/ui/src/app/metadata/manager/component/entity-item.component.html index 41fdcd3d6..5be89b505 100644 --- a/ui/src/app/metadata/manager/component/entity-item.component.html +++ b/ui/src/app/metadata/manager/component/entity-item.component.html @@ -14,11 +14,11 @@
{{ entity.name }} + 'fa-check-circle text-success': entity.enabled, + 'fa-times-circle text-danger': !entity.enabled + }"> Incomplete Form - {{ entity.getId() }} + {{ entity.getDisplayId() }}
@@ -37,7 +37,7 @@
- {entity.kind, select, filter {Filter} provider {Service Provider} other {other}} Name: + Metadata Source Name:
{{ entity.name }}
Created Date:
@@ -46,11 +46,11 @@
- {entity.kind, select, filter {Filter} provider {Service Provider} other {other}} Entity ID: + Metadata Source Entity ID:
-
{{ entity.getId() }}
+
{{ entity.getDisplayId() }}
- {entity.kind, select, filter {Filter} provider {Service Provider} other {other}} Status: + Metadata Source Status:
diff --git a/ui/src/app/metadata/resolver/container/new-resolver.component.html b/ui/src/app/metadata/resolver/container/new-resolver.component.html index 48f0b847d..da87cfc6c 100644 --- a/ui/src/app/metadata/resolver/container/new-resolver.component.html +++ b/ui/src/app/metadata/resolver/container/new-resolver.component.html @@ -16,7 +16,7 @@

How are you addi