diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/NameIdFormatFilterUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/NameIdFormatFilterUiDefinitionController.groovy new file mode 100644 index 000000000..3dcfcaffe --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/NameIdFormatFilterUiDefinitionController.groovy @@ -0,0 +1,60 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation +import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry +import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +import javax.annotation.PostConstruct + +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.nameIdFormatFilterSchema +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR + +/** + * Controller implementing REST resource responsible for exposing structure definition for nameid format filter user + * interface in terms of JSON schema. + * + * @author Dmitriy Kopylenko + */ +@RestController +@RequestMapping('/api/ui/NameIdFormatFilter') +@Slf4j +class NameIdFormatFilterUiDefinitionController { + + @Autowired + JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry + + JsonSchemaResourceLocation jsonSchemaLocation + + @Autowired + ObjectMapper jacksonObjectMapper + + @Autowired + JsonSchemaBuilderService jsonSchemaBuilderService + + @GetMapping + ResponseEntity getUiDefinitionJsonSchema() { + try { + def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) + jsonSchemaBuilderService.addRelyingPartyOverridesCollectionDefinitionsToJson(parsedJson["definitions"]) + return ResponseEntity.ok(parsedJson) + } + catch (Exception e) { + log.error(e.getMessage(), e) + return ResponseEntity.status(INTERNAL_SERVER_ERROR) + .body([jsonParseError : e.getMessage(), + sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) + } + } + + @PostConstruct + void init() { + this.jsonSchemaLocation = nameIdFormatFilterSchema(this.jsonSchemaResourceLocationRegistry) + } +} 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 48a74d793..4b41a9e29 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 @@ -5,8 +5,10 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration 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.filters.NameIdFormatFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.opensaml.OpenSamlNameIdFormatFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver @@ -28,6 +30,7 @@ import org.opensaml.saml.common.profile.logic.EntityIdPredicate import org.opensaml.saml.metadata.resolver.MetadataResolver import org.opensaml.saml.metadata.resolver.filter.MetadataFilter import org.opensaml.saml.metadata.resolver.filter.MetadataFilterChain +import org.opensaml.saml.metadata.resolver.filter.impl.NameIDFormatFilter import org.opensaml.saml.saml2.core.Attribute import org.opensaml.saml.saml2.metadata.EntityDescriptor import org.springframework.beans.factory.annotation.Autowired @@ -35,6 +38,9 @@ import org.w3c.dom.Document import javax.annotation.Nonnull +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.CONDITION_SCRIPT +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.REGEX import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver.ResourceType.CLASSPATH import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver.ResourceType.SVN @@ -60,7 +66,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { @Override void reloadFilters(String metadataResolverResourceId) { OpenSamlChainingMetadataResolver chainingMetadataResolver = (OpenSamlChainingMetadataResolver) metadataResolver - MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find { it.id == metadataResolverResourceId } + MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find { + it.id == metadataResolverResourceId + } edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver jpaMetadataResolver = metadataResolverRepository.findByResourceId(metadataResolverResourceId) if (targetMetadataResolver && targetMetadataResolver.getMetadataFilter() instanceof MetadataFilterChain) { @@ -103,6 +111,30 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { target.setRules(rules) metadataFilters.add(target) } + if (metadataFilter instanceof NameIdFormatFilter) { + NameIdFormatFilter nameIdFormatFilter = NameIdFormatFilter.cast(metadataFilter) + NameIDFormatFilter openSamlTargetFilter = new OpenSamlNameIdFormatFilter() + openSamlTargetFilter.removeExistingFormats = nameIdFormatFilter.removeExistingFormats + Map, Collection> predicateRules = [:] + def type = nameIdFormatFilter.nameIdFormatFilterTarget.nameIdFormatFilterTargetType + def values = nameIdFormatFilter.nameIdFormatFilterTarget.value + switch (type) { + case ENTITY: + predicateRules[new EntityIdPredicate(values)] = nameIdFormatFilter.formats + break + case CONDITION_SCRIPT: + predicateRules[new ScriptedPredicate(new EvaluableScript(values[0]))] = nameIdFormatFilter.formats + break + case REGEX: + predicateRules[new ScriptedPredicate(new EvaluableScript(generateJavaScriptRegexScript(values[0])))] = nameIdFormatFilter.formats + break + default: + // do nothing, we'd have exploded elsewhere previously. + break + } + openSamlTargetFilter.rules = predicateRules + metadataFilters << openSamlTargetFilter + } } metadataFilterChain.setFilters(metadataFilters) } @@ -190,7 +222,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) { - if(filter.xmlShouldBeGenerated()) { + if (filter.xmlShouldBeGenerated()) { markupBuilderDelegate.MetadataFilter(id: filter.name, 'xsi:type': 'SignatureValidation', 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata', @@ -261,7 +293,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } void constructXmlNodeForFilter(RequiredValidUntilFilter filter, def markupBuilderDelegate) { - if(filter.xmlShouldBeGenerated()) { + if (filter.xmlShouldBeGenerated()) { markupBuilderDelegate.MetadataFilter( 'xsi:type': 'RequiredValidUntil', maxValidityInterval: filter.maxValidityInterval @@ -269,6 +301,44 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForFilter(NameIdFormatFilter filter, def markupBuilderDelegate) { + def type = filter.nameIdFormatFilterTarget.nameIdFormatFilterTargetType + markupBuilderDelegate.MetadataFilter( + 'xsi:type': 'NameIDFormat', + 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata', + 'removeExistingFormats': filter.removeExistingFormats ?: null + ) { + filter.formats.each { + Format(it) + } + switch (type) { + case ENTITY: + filter.nameIdFormatFilterTarget.value.each { + Entity(it) + } + break + case CONDITION_SCRIPT: + case REGEX: + ConditionScript() { + Script() { + def script + def scriptValue = filter.nameIdFormatFilterTarget.value[0] + if (type == CONDITION_SCRIPT) { + script = scriptValue + } else if (type == REGEX) { + script = generateJavaScriptRegexScript(scriptValue) + } + mkp.yieldUnescaped("\n\n") + } + } + break + default: + // do nothing, we'd have exploded elsewhere previously. + break + } + } + } + void constructXmlNodeForResolver(FilesystemMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { markupBuilderDelegate.MetadataProvider(id: resolver.xmlId, 'xsi:type': 'FilesystemMetadataProvider', @@ -486,4 +556,5 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java index f179c7880..e1483d0b4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/JsonSchemaComponentsConfiguration.java @@ -15,6 +15,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER; /** * @author Dmitriy Kopylenko @@ -46,7 +47,12 @@ public class JsonSchemaComponentsConfiguration { //Configured via @ConfigurationProperties (using setter method) with 'shibui.dynamic-http-metadata-provider-ui-schema-location' property and // default value set here if that property is not explicitly set in application.properties @Setter - private String dynamicHttpMetadataResolverUiSchemaLocation = "classpath:dynamic-http-metadata-provider.schema.json"; + private String dynamicHttpMetadataResolverUiSchemaLocation = "classpath:nameid-filter.schema.json"; + + //Configured via @ConfigurationProperties (using setter method) with 'shibui.nameid-filter-ui-schema-location' property and + // default value set here if that property is not explicitly set in application.properties + @Setter + private String nameIdFormatFilterUiSchemaLocation = "classpath:nameid-filter.schema.json"; @Bean public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) { @@ -80,6 +86,12 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res .resourceLoader(resourceLoader) .jacksonMapper(jacksonMapper) .detectMalformedJson(true) + .build()) + .register(NAME_ID_FORMAT_FILTER, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation(nameIdFormatFilterUiSchemaLocation) + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(true) .build()); } 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 431632023..57d734f77 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 @@ -3,6 +3,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; 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.filters.NameIdFormatFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; @@ -224,6 +225,13 @@ else if(filterWithUpdatedData instanceof RequiredValidUntilFilter) { RequiredValidUntilFilter fromFilter = RequiredValidUntilFilter.class.cast(filterWithUpdatedData); toFilter.setMaxValidityInterval(fromFilter.getMaxValidityInterval()); } + else if (filterWithUpdatedData instanceof NameIdFormatFilter) { + NameIdFormatFilter toFilter = NameIdFormatFilter.class.cast(filterToBeUpdated); + NameIdFormatFilter fromFilter = NameIdFormatFilter.class.cast(filterWithUpdatedData); + toFilter.setRemoveExistingFormats(fromFilter.getRemoveExistingFormats()); + toFilter.setFormats(fromFilter.getFormats()); + toFilter.setNameIdFormatFilterTarget(fromFilter.getNameIdFormatFilterTarget()); + } //TODO: add other types of concrete filters update here } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java index 63c1ffa36..1e8f4c2a0 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java @@ -116,6 +116,8 @@ public List getOrderedChildren() { return null; } + //TODO: refactor mappings so we're also able persist and use non-null parent which happens to be used in some code paths + //where we use open saml API (re-filtering, for example) @Transient private transient XMLObject parent; @Nullable diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java index a80d538c9..8a42cd1f7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java @@ -32,7 +32,8 @@ @JsonSubTypes({@JsonSubTypes.Type(value=EntityRoleWhiteListFilter.class, name="EntityRoleWhiteList"), @JsonSubTypes.Type(value=EntityAttributesFilter.class, name="EntityAttributes"), @JsonSubTypes.Type(value=SignatureValidationFilter.class, name="SignatureValidation"), - @JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil")}) + @JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil"), + @JsonSubTypes.Type(value=NameIdFormatFilter.class, name="NameIDFormat")}) public class MetadataFilter extends AbstractAuditable { @JsonProperty("@type") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java new file mode 100644 index 000000000..608561407 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java @@ -0,0 +1,35 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.CascadeType; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.OneToOne; +import javax.persistence.OrderColumn; +import java.util.List; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +@ToString +public class NameIdFormatFilter extends MetadataFilter { + + public NameIdFormatFilter() { + type = "NameIDFormat"; + } + + private Boolean removeExistingFormats = false; + + @ElementCollection + @OrderColumn + private List formats; + + @OneToOne(cascade = CascadeType.ALL) + private NameIdFormatFilterTarget nameIdFormatFilterTarget; + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java new file mode 100644 index 000000000..a346d983f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java @@ -0,0 +1,51 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.OrderColumn; +import java.util.ArrayList; +import java.util.List; + +@Entity +@EqualsAndHashCode(callSuper = true) +@ToString +public class NameIdFormatFilterTarget extends AbstractAuditable { + + public enum NameIdFormatFilterTargetType { + ENTITY, CONDITION_SCRIPT, REGEX + } + + private NameIdFormatFilterTargetType nameIdFormatFilterTargetType; + + public NameIdFormatFilterTargetType getNameIdFormatFilterTargetType() { + return nameIdFormatFilterTargetType; + } + + public void setNameIdFormatFilterTargetType(NameIdFormatFilterTargetType nameIdFormatFilterTargetType) { + this.nameIdFormatFilterTargetType = nameIdFormatFilterTargetType; + } + + @ElementCollection + @OrderColumn + private List value; + + public List getValue() { + return value; + } + + public void setSingleValue(String value) { + List values = new ArrayList<>(); + values.add(value); + this.value = values; + } + + public void setValue(List value) { + this.value = value; + } + + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/opensaml/OpenSamlNameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/opensaml/OpenSamlNameIdFormatFilter.java new file mode 100644 index 000000000..90a5f6c18 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/opensaml/OpenSamlNameIdFormatFilter.java @@ -0,0 +1,91 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters.opensaml; + +import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements; +import net.shibboleth.utilities.java.support.component.ComponentSupport; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.SAMLObjectBuilder; +import org.opensaml.saml.metadata.resolver.filter.FilterException; +import org.opensaml.saml.metadata.resolver.filter.impl.NameIDFormatFilter; +import org.opensaml.saml.saml2.metadata.AttributeAuthorityDescriptor; +import org.opensaml.saml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml.saml2.metadata.NameIDFormat; +import org.opensaml.saml.saml2.metadata.PDPDescriptor; +import org.opensaml.saml.saml2.metadata.RoleDescriptor; +import org.opensaml.saml.saml2.metadata.SPSSODescriptor; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; + +/** + * Extension to open saml type for workaround forced component initialization check. We need to override filter + * method to skip this check as we use re-filtering in Shib UI context just to reload effective metadata. + * + * @author Dmitriy Kopylenko + */ +public class OpenSamlNameIdFormatFilter extends NameIDFormatFilter { + + private boolean removeExistingFormats; + + @Nonnull private final SAMLObjectBuilder formatBuilder; + + public OpenSamlNameIdFormatFilter() { + formatBuilder = (SAMLObjectBuilder) + XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilderOrThrow( + NameIDFormat.DEFAULT_ELEMENT_NAME); + } + + @Override + public void setRemoveExistingFormats(final boolean flag) { + ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this); + removeExistingFormats = flag; + } + + @Nullable + @Override + public XMLObject filter(@Nullable XMLObject metadata) throws FilterException { + if (metadata == null) { + return null; + } + + if (metadata instanceof EntitiesDescriptor) { + filterEntitiesDescriptor((EntitiesDescriptor) metadata); + } else { + filterEntityDescriptor((EntityDescriptor) metadata); + } + + return metadata; + } + + /**Overridden to get rid of log statements which result in NPE in the base class with parent being null + * TODO: remove this method once the parent mapping in AbstractXmlObject is implemented. + */ + @Override + protected void filterRoleDescriptor(@Nonnull final RoleDescriptor role, + @Nonnull @NonnullElements final Collection formats) { + + final Collection roleFormats; + + if (role instanceof SPSSODescriptor) { + roleFormats = ((SPSSODescriptor) role).getNameIDFormats(); + } else if (role instanceof AttributeAuthorityDescriptor) { + roleFormats = ((AttributeAuthorityDescriptor) role).getNameIDFormats(); + } else if (role instanceof PDPDescriptor) { + roleFormats = ((PDPDescriptor) role).getNameIDFormats(); + } else { + return; + } + + if (removeExistingFormats && !roleFormats.isEmpty()) { + roleFormats.clear(); + } + + for (final String format : formats) { + final NameIDFormat nif = formatBuilder.buildObject(); + nif.setFormat(format); + roleFormats.add(nif); + } + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java index 12c5ae67d..71986eea7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaLocationLookup.java @@ -5,6 +5,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER; import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER; +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER; /** * Utility methods for common JSON schema types lookups. @@ -77,4 +78,17 @@ public static JsonSchemaResourceLocation dynamicHttpMetadataProviderSchema(JsonS .lookup(DYNAMIC_HTTP_METADATA_RESOLVER) .orElseThrow(() -> new IllegalStateException("JSON schema resource location for dynamic http metadata resolver is not registered.")); } + + /** + * Searches name id format filter JSON schema resource location object in the given location registry. + * + * @param resourceLocationRegistry + * @return name id format filter JSON schema resource location object + * @throws IllegalStateException if schema is not found in the given registry + */ + public static JsonSchemaResourceLocation nameIdFormatFilterSchema(JsonSchemaResourceLocationRegistry resourceLocationRegistry) { + return resourceLocationRegistry + .lookup(NAME_ID_FORMAT_FILTER) + .orElseThrow(() -> new IllegalStateException("JSON schema resource location for name id format filter is not registered.")); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java index ad0f4cd84..2d25fac0b 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/jsonschema/JsonSchemaResourceLocation.java @@ -37,7 +37,6 @@ public JsonSchemaResourceLocation(String jsonSchemaLocation, ResourceLoader reso this.jacksonMapper = jacksonMapper; } - //This constructor is used in tests public JsonSchemaResourceLocation(String jsonSchemaLocation, ResourceLoader resourceLoader, ObjectMapper jacksonMapper, @@ -99,6 +98,7 @@ public enum SchemaType { // filter types ENTITY_ATTRIBUTES_FILTERS("EntityAttributesFilters"), + NAME_ID_FORMAT_FILTER("NameIdFormatFilter"), // resolver types FILE_BACKED_HTTP_METADATA_RESOLVER("FileBackedHttpMetadataResolver"), diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 9ef60951e..81a0d09dd 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -53,6 +53,7 @@ shibui.logout-url=/dashboard shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema.json shibui.entity-attributes-filters-ui-schema-location=classpath:entity-attributes-filters-ui-schema.json +shibui.nameid-filter-ui-schema-location=classpath:nameid-filter.schema.json #Actuator endpoints (info) # Un-comment to get full git details exposed like author, abbreviated SHA-1, commit message diff --git a/backend/src/main/resources/entity-attributes-filters-ui-schema.json b/backend/src/main/resources/entity-attributes-filters-ui-schema.json index 5d71f0ba8..d67364b21 100644 --- a/backend/src/main/resources/entity-attributes-filters-ui-schema.json +++ b/backend/src/main/resources/entity-attributes-filters-ui-schema.json @@ -1,9 +1,6 @@ { "title": "EntityAttributes Filter", "type": "object", - "widget": { - "id": "fieldset" - }, "properties": { "name": { "title": "label.filter-name", @@ -44,7 +41,8 @@ "description": "tooltip.search-criteria", "type": "object", "widget": { - "id": "filter-target" + "id": "filter-target", + "target": "entityAttributesFilterTargetType" }, "properties": { "entityAttributesFilterTargetType": { diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index fd8100dcf..adeae5ce9 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -69,6 +69,10 @@ value.signing=Signing value.encryption=Encryption value.both=Both +value.entity=Entity +value.condition-ref=ConditionRef +value.condition-script=ConditionScript + value.file-backed-http-metadata-provider=FileBackedHttpMetadataProvider value.file-system-metadata-provider=FileSystemMetadataProvider value.local-dynamic-metadata-provider=LocalDynamicMetadataProvider @@ -130,6 +134,7 @@ label.clear-all-attributes=Clear All Attributes label.protocol-support-enumeration=Protocol Support Enumeration label.select-protocol=Select Protocol label.nameid-format=NameID Format +label.nameid-formats=NameID Formats label.name-and-entity-id=Name and Entity ID label.organization-information=Organization Information label.contact-information=Contact Information @@ -365,6 +370,12 @@ label.encoding-style=Encoding Style label.velocity-engine=Velocity Engine label.match=Match +label.remove-existing-formats=Remove Existing Formats? +label.nameid-formats-format=NameID Format +label.nameid-formats-value=NameID Value +label.nameid-formats-type=NameID Type + +label.select-filter-type=Select Filter Type message.must-be-unique=Must be unique. message.name-must-be-unique=Name must be unique. @@ -422,7 +433,7 @@ tooltip.assertion-consumer-service-location=Assertion Consumer Service Location tooltip.assertion-consumer-service-location-binding=Assertion Consumer Service Location Binding tooltip.mark-as-default=Mark as Default tooltip.protocol-support-enumeration=Protocol Support Enumeration -tooltip.nameid-format=Add NameID Format +tooltip.nameid-format=Content is name identifier format which is added to all the applicable roles of the entities which match any of the following or {{}}elements. tooltip.enable-this-service-upon-saving=Enable this service upon saving tooltip.authentication-requests-signed=Authentication Requests Signed tooltip.want-assertions-signed=Want Assertions Signed @@ -514,4 +525,9 @@ tooltip.md-request-value=Content of the element. tooltip.transform-ref=A reference to a transform function for the entityID. If used, the child element must be empty. tooltip.encoding-style=Determines whether and how the entityID value will be URL encoded prior to replacement. Allowed values are: 1) "none" - no encoding is performed, 2) "form" - encoded using URL form parameter encoding (for query parameters), 3) "path" - encoded using URL path encoding, or 4) "fragment" - encoded using URL fragment encoding. The precise definition of these terms is defined in the documentation for the methods of the Guava library\u0027s UrlEscapers class. tooltip.velocity-engine=This attribute may be used to specify the name of the Velocity engine defined within the application. -tooltip.match=A regular expression against which the entityID is evaluated. \ No newline at end of file +tooltip.match=A regular expression against which the entityID is evaluated. + +tooltip.remove-existing-formats=Whether to remove any existing formats from a role if any are added by the filter (unmodified roles will be untouched regardless of this setting) +tooltip.nameid-formats-format=Format +tooltip.nameid-formats-value=Value +tooltip.nameid-formats-type=Type \ No newline at end of file diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json new file mode 100644 index 000000000..db562abe5 --- /dev/null +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -0,0 +1,127 @@ +{ + "type": "object", + "fieldsets": [ + { + "type": "group-lg", + "fields": [ + "name", + "nameIdFormatFilterTarget", + "filterEnabled", + "@type", + "resourceId", + "version", + "removeExistingFormats" + ] + }, + { + "type": "group", + "fields": [ + "formats" + ] + } + ], + "required": [ + "name" + ], + "properties": { + "name": { + "title": "label.filter-name", + "description": "tooltip.filter-name", + "type": "string", + "widget": { + "id": "string", + "help": "message.must-be-unique" + } + }, + "filterEnabled": { + "title": "label.enable-filter", + "description": "tooltip.enable-filter", + "type": "boolean", + "default": false + }, + "nameIdFormatFilterTarget": { + "title": "label.search-criteria", + "description": "tooltip.search-criteria", + "type": "object", + "widget": { + "id": "filter-target", + "target": "nameIdFormatFilterTargetType" + }, + "properties": { + "nameIdFormatFilterTargetType": { + "title": "", + "type": "string", + "default": "ENTITY", + "oneOf": [ + { + "enum": [ + "ENTITY" + ], + "description": "value.entity-id" + }, + { + "enum": [ + "REGEX" + ], + "description": "value.regex" + }, + { + "enum": [ + "CONDITION_SCRIPT" + ], + "description": "value.script" + } + ] + }, + "value": { + "type": "array", + "buttons": [ + { + "id": "preview", + "label": "action.preview", + "widget": "icon-button" + } + ], + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string" + } + } + }, + "required": [ + "value", + "nameIdFormatFilterTargetType" + ] + }, + "@type": { + "type": "string", + "widget": { + "id": "hidden" + }, + "default": "NameIDFormat" + }, + "version": { + "type": "integer", + "widget": { + "id": "hidden" + } + }, + "resourceId": { + "type": "string", + "widget": { + "id": "hidden" + } + }, + "removeExistingFormats": { + "type": "boolean", + "title": "label.remove-existing-formats", + "description": "tooltip.remove-existing-formats", + "default": false + }, + "formats": { + "$ref": "#/definitions/nameIdFormats" + } + }, + "definitions": {} +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy index 671961e8b..af758f131 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests.groovy @@ -17,6 +17,7 @@ import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResour import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.METADATA_SOURCES +import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER /** * @author Dmitriy Kopylenko @@ -78,6 +79,13 @@ class BadJSONMetadataSourcesUiDefinitionControllerIntegrationTests extends Speci .jacksonMapper(jacksonMapper) .detectMalformedJson(false) .build()) + .register(NAME_ID_FORMAT_FILTER, JsonSchemaLocationBuilder.with() + .jsonSchemaLocation('classpath:nameid-filter.schema.json') + .resourceLoader(resourceLoader) + .jacksonMapper(jacksonMapper) + .detectMalformedJson(false) + .build()) + } } } \ No newline at end of file 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 db1f15ab9..802afdb81 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 @@ -167,6 +167,7 @@ class MetadataFiltersControllerTests extends Specification { .content(postedJsonBody)) then: + println postedJsonBody result.andExpect(status().isCreated()) .andExpect(content().json(expectedJsonBody, true)) .andExpect(header().string(expectedResponseHeader, containsString(expectedResponseHeaderValue))) @@ -177,6 +178,7 @@ class MetadataFiltersControllerTests extends Specification { 'entityRoleWhiteList' | _ 'signatureValidation' | _ 'requiredValidUntil' | _ + 'nameIdFormat' | _ } @Unroll @@ -222,6 +224,7 @@ class MetadataFiltersControllerTests extends Specification { 'entityRoleWhiteList' | _ 'signatureValidation' | _ 'requiredValidUntil' | _ + 'nameIdFormat' | _ } def "FilterController.update filter 409's if the version numbers don't match"() { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy index b3afd2adf..9c1ba5342 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/filters/PolymorphicFiltersJacksonHandlingTests.groovy @@ -125,6 +125,7 @@ class PolymorphicFiltersJacksonHandlingTests extends Specification { json.contains('EntityAttributes') json.contains('RequiredValidUntil') json.contains('EntityAttributes') + json.contains('NameIDFormat') } @@ -174,4 +175,45 @@ class PolymorphicFiltersJacksonHandlingTests extends Specification { EntityAttributesFilter.class.cast(filter).entityAttributesFilterTarget.entityAttributesFilterTargetType.name() == 'ENTITY' EntityAttributesFilter.class.cast(filter).entityAttributesFilterTarget.value == ['GATCCLk32V'] } + + def "Correct polymorphic serialization of NameIdFormatFilter"() { + given: + def givenFilterJson = """ + { + "createdDate" : null, + "modifiedDate" : null, + "createdBy" : null, + "modifiedBy" : null, + "name" : "NameIDFormat", + "resourceId" : "ab95b80f-102b-494c-a3b8-27b625553977", + "filterEnabled" : false, + "removeExistingFormats" : false, + "formats" : [ "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ], + "nameIdFormatFilterTarget" : { + "createdDate" : null, + "modifiedDate" : null, + "createdBy" : null, + "modifiedBy" : null, + "nameIdFormatFilterTargetType" : "ENTITY", + "value" : [ "https://sp1.example.org" ], + "audId" : null + }, + "audId" : null, + "@type" : "NameIDFormat", + "version" : 1896953777 + }""" + + when: + def deSerializedFilter = mapper.readValue(givenFilterJson, MetadataFilter) + def json = mapper.writeValueAsString(deSerializedFilter) + println(json) + def roundTripFilter = mapper.readValue(json, MetadataFilter) + + then: + roundTripFilter == deSerializedFilter + + and: + deSerializedFilter instanceof NameIdFormatFilter + roundTripFilter instanceof NameIdFormatFilter + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy index d7b59df55..d39cddc93 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy @@ -6,6 +6,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.Internationalization import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -15,6 +16,8 @@ import spock.lang.Specification import javax.persistence.EntityManager +import static edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator.* + @DataJpaTest @ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @@ -70,4 +73,16 @@ class FilterRepositoryTests extends Specification { item1.hashCode() == item2.hashCode() } + + def "NameIdFormatFilter is able to be persisted to RDBMS"() { + given: + def nameIdFormatFilter = TestObjectGenerator.nameIdFormatFilter() + + when: + def persistedFilter = repositoryUnderTest.save(nameIdFormatFilter) + + then: + persistedFilter.audId > 0L + persistedFilter.formats.size() == 1 + } } 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 94dde95d8..cbeac0960 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 @@ -214,6 +214,17 @@ class JPAMetadataResolverServiceImplTests extends Specification { generatedXmlIsTheSameAsExpectedXml('/conf/552.xml', domBuilder.parseText(writer.toString())) } + def 'test generating NameIdFormatFilter xml snippet'() { + given: + def filter = TestObjectGenerator.nameIdFormatFilter() + + when: + genXmlSnippet(markupBuilder) { JPAMetadataResolverServiceImpl.cast(metadataResolverService).constructXmlNodeForFilter(filter, it) } + + then: + generatedXmlIsTheSameAsExpectedXml('/conf/799.xml', domBuilder.parseText(writer.toString())) + } + def 'test generating FileBackedHttMetadataResolver xml snippet'() { given: def resolver = testObjectGenerator.fileBackedHttpMetadataResolver() 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 071516cc2..8eb8426f4 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 @@ -15,6 +15,8 @@ import org.opensaml.saml.saml2.metadata.Organization import java.nio.file.Files import java.util.function.Supplier +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY + /** * @author Bill Smith (wsmith@unicon.net) */ @@ -130,6 +132,7 @@ class TestObjectGenerator { filterList.add(buildFilter { entityRoleWhitelistFilter() }) filterList.add(buildFilter { signatureValidationFilter() }) filterList.add(buildFilter { requiredValidUntilFilter() }) + filterList.add(buildFilter { nameIdFormatFilter() }) } return filterList } @@ -149,6 +152,9 @@ class TestObjectGenerator { case 'requiredValidUntil': randomFilter = requiredValidUntilFilter() break + case 'nameIdFormat': + randomFilter = nameIdFormatFilter() + break default: throw new RuntimeException("Did you forget to create a TestObjectGenerator.copyOf method for filtertype: ${filterType} ?"); } @@ -168,6 +174,7 @@ class TestObjectGenerator { it } } + EntityRoleWhiteListFilter entityRoleWhitelistFilter() { new EntityRoleWhiteListFilter().with { it.name = 'EntityRoleWhiteList' @@ -212,6 +219,20 @@ class TestObjectGenerator { } } + static NameIdFormatFilter nameIdFormatFilter() { + return new NameIdFormatFilter().with { + it.name = "NameIDFormat" + it.formats = ['urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'] + it.nameIdFormatFilterTarget = new NameIdFormatFilterTarget(nameIdFormatFilterTargetType: ENTITY, singleValue: 'https://sp1.example.org') + + /*it.name = "NameIDFormat" + it.formats = ['urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', 'urn:oasis:names:tc:SAML:2.0:nameid-format:email'] + it.nameIdFormatFilterTarget = new NameIdFormatFilterTarget(nameIdFormatFilterTargetType: CONDITION_SCRIPT, singleValue: 'eval(true);')*/ + + it + } + } + RequiredValidUntilFilter copyOf(RequiredValidUntilFilter requiredValidUntilFilter) { new RequiredValidUntilFilter().with { it.name = requiredValidUntilFilter.name @@ -258,6 +279,16 @@ class TestObjectGenerator { } } + static NameIdFormatFilter copyOf(NameIdFormatFilter nameIdFormatFilter) { + new NameIdFormatFilter().with { + it.name = nameIdFormatFilter.name + it.resourceId = nameIdFormatFilter.resourceId + it.removeExistingFormats = nameIdFormatFilter.removeExistingFormats + it.formats = nameIdFormatFilter.formats + it + } + } + MetadataFilter buildFilter(Supplier filterSupplier) { MetadataFilter filter = filterSupplier.get() filter.setFilterEnabled(generator.randomBoolean()) @@ -268,12 +299,12 @@ class TestObjectGenerator { List buildAttributesList() { List attributes = new ArrayList<>() - customPropertiesConfiguration.getOverrides().each {override -> + customPropertiesConfiguration.getOverrides().each { override -> if (generator.randomBoolean()) { switch (ModelRepresentationConversions.AttributeTypes.valueOf(override.getDisplayType().toUpperCase())) { case ModelRepresentationConversions.AttributeTypes.BOOLEAN: if (override.getPersistType() != null && - override.getPersistType() != override.getDisplayType()) { + override.getPersistType() != override.getDisplayType()) { attributes.add(attributeUtility.createAttributeWithStringValues(override.getAttributeName(), override.getAttributeFriendlyName(), generator.randomString(30))) } else { attributes.add(attributeUtility.createAttributeWithBooleanValue(override.getAttributeName(), override.getAttributeFriendlyName(), generator.randomBoolean())) @@ -318,7 +349,7 @@ class TestObjectGenerator { switch (ModelRepresentationConversions.AttributeTypes.valueOf(override.getDisplayType().toUpperCase())) { case ModelRepresentationConversions.AttributeTypes.BOOLEAN: if (override.getPersistType() != null && - override.getPersistType() != override.getDisplayType()) { + override.getPersistType() != override.getDisplayType()) { representation.put(override.getName(), generator.randomString(30)) } else { representation.put(override.getName(), generator.randomBoolean()) diff --git a/backend/src/test/resources/conf/799.xml b/backend/src/test/resources/conf/799.xml new file mode 100644 index 000000000..90f43f50d --- /dev/null +++ b/backend/src/test/resources/conf/799.xml @@ -0,0 +1,12 @@ + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + https://sp1.example.org + + diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 48e341a09..000000000 --- a/package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lockfileVersion": 1 -} diff --git a/ui/package-lock.json b/ui/package-lock.json index bb8441756..fa183d636 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -183,6 +183,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -191,6 +192,537 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } } }, "expand-brackets": { @@ -525,6 +1057,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -533,6 +1066,537 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } } }, "expand-brackets": { @@ -922,6 +1986,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -930,18 +1995,549 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" @@ -1472,6 +3068,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -1480,6 +3077,537 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } } }, "expand-brackets": { @@ -2807,6 +4935,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", + "fsevents": "1.2.4", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -4630,12 +6759,541 @@ "readable-stream": "2.3.3" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, "fstream": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", @@ -7361,6 +10019,13 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "nan": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", + "dev": true, + "optional": true + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -11071,6 +13736,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -11079,6 +13745,537 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } } }, "expand-brackets": { @@ -11918,6 +15115,7 @@ "anymatch": "2.0.0", "async-each": "1.0.1", "braces": "2.3.2", + "fsevents": "1.2.4", "glob-parent": "3.1.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -11926,6 +15124,537 @@ "path-is-absolute": "1.0.1", "readdirp": "2.1.0", "upath": "1.0.5" + }, + "dependencies": { + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.2.4" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + } } }, "cliui": { diff --git a/ui/src/app/metadata/domain/domain.util.ts b/ui/src/app/metadata/domain/domain.util.ts index af9ab0153..2aeeac247 100644 --- a/ui/src/app/metadata/domain/domain.util.ts +++ b/ui/src/app/metadata/domain/domain.util.ts @@ -11,12 +11,17 @@ export const getInCollectionFn = (entities, selectedId) => { }; export const getEntityIdsFn = list => list.map(entity => entity.entityId); +export const getId = (entity: Metadata): string => { + return entity.resourceId ? entity.resourceId : entity.id; +}; + export const mergeOrderFn = (entities: Metadata[], order: string[]): Metadata[] => { - return [...entities.sort( + const ordered = [...entities.sort( (a: Metadata, b: Metadata) => { - const aIndex = order.indexOf(a.id); - const bIndex = order.indexOf(b.id); + const aIndex = order.indexOf(getId(a)); + const bIndex = order.indexOf(getId(b)); return aIndex > bIndex ? 1 : bIndex > aIndex ? -1 : 0; } )]; + return ordered; }; diff --git a/ui/src/app/metadata/domain/entity/filter/nameid-format-filter.ts b/ui/src/app/metadata/domain/entity/filter/nameid-format-filter.ts new file mode 100644 index 000000000..d57b1e345 --- /dev/null +++ b/ui/src/app/metadata/domain/entity/filter/nameid-format-filter.ts @@ -0,0 +1,65 @@ +import { MetadataFilter, MetadataEntity } from '../../model'; +import { MetadataTypes } from '../../domain.type'; +import { FilterTarget } from '../../model'; + +export class NameIDFormatFilterEntity implements MetadataFilter, MetadataEntity { + createdDate?: string; + modifiedDate?: string; + version: string; + resourceId: string; + + name = ''; + filterEnabled = false; + audId: string; + type: string; + + nameIdFormatFilterTarget: FilterTarget = { + type: 'ENTITY', + value: [''] + }; + + constructor(obj?: Partial) { + Object.assign(this, { ...obj }); + } + + getId(): string { + return this.resourceId; + } + + getDisplayId(): string { + return this.resourceId; + } + + isDraft(): boolean { + return false; + } + + getCreationDate(): Date { + return new Date(this.createdDate); + } + + get id(): string { + return this.resourceId; + } + + set id(id: string) { + this.resourceId = id; + } + + get enabled(): boolean { + return this.filterEnabled; + } + + get kind(): string { + return MetadataTypes.FILTER; + } + + serialize(): any { + return { + nameIdFormatFilterTarget: this.nameIdFormatFilterTarget, + filterEnabled: this.filterEnabled, + name: this.name, + '@type': 'EntityAttributes' + }; + } +} diff --git a/ui/src/app/metadata/domain/model/metadata-filter.ts b/ui/src/app/metadata/domain/model/metadata-filter.ts index 5cb50e2ea..46e53c4fb 100644 --- a/ui/src/app/metadata/domain/model/metadata-filter.ts +++ b/ui/src/app/metadata/domain/model/metadata-filter.ts @@ -4,7 +4,7 @@ export interface MetadataFilter extends MetadataBase { name: string; filterEnabled?: boolean; type: string; - resourceId: string; + resourceId: string | null; [key: string]: any; } diff --git a/ui/src/app/metadata/domain/model/metadata-resolver.ts b/ui/src/app/metadata/domain/model/metadata-resolver.ts index 9f8e0686c..01505cff5 100644 --- a/ui/src/app/metadata/domain/model/metadata-resolver.ts +++ b/ui/src/app/metadata/domain/model/metadata-resolver.ts @@ -12,6 +12,7 @@ import { export interface MetadataResolver extends MetadataBase { id: string; + resourceId?: string; entityId?: string; serviceProviderName: string; organization?: Organization; diff --git a/ui/src/app/metadata/filter/action/filter.action.ts b/ui/src/app/metadata/filter/action/filter.action.ts index ad9ef8d28..1034d9e09 100644 --- a/ui/src/app/metadata/filter/action/filter.action.ts +++ b/ui/src/app/metadata/filter/action/filter.action.ts @@ -4,6 +4,7 @@ import { MDUI } from '../../domain/model'; export enum FilterActionTypes { SELECT_ID = '[Filter] Select Entity ID', + SELECT_FILTER_TYPE = '[Filter] Select Filter Type', UPDATE_FILTER = '[Filter] Update Filter', CANCEL_CREATE_FILTER = '[Filter] Cancel Create Filter', LOAD_ENTITY_PREVIEW = '[Filter] Load Preview data', @@ -43,8 +44,15 @@ export class UpdateFilterChanges implements Action { constructor(public payload: Partial) { } } +export class SelectFilterType implements Action { + readonly type = FilterActionTypes.SELECT_FILTER_TYPE; + + constructor(public payload: string) { } +} + export type FilterActionsUnion = | SelectId + | SelectFilterType | UpdateFilterChanges | CancelCreateFilter | LoadEntityPreview diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index a7771dbef..f0eea6d90 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -5,33 +5,47 @@
   - Edit EntityAttributesFilter + + Edit Filter– {{ type$ | async }} +
-
- -   - +
+
+
+
+ + +
+
+
+ + +   + + +
+
+
-
diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts b/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts index b38fb4256..660bf53fb 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts @@ -11,8 +11,9 @@ import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-sch import { SchemaService } from '../../../schema-form/service/schema.service'; import { HttpClientModule } from '@angular/common/http'; import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { MetadataFilterTypes } from '../model'; -describe('New Metadata Filter Page', () => { +describe('Edit Metadata Filter Page', () => { let fixture: ComponentFixture; let store: Store; let instance: EditFilterComponent; @@ -25,8 +26,7 @@ describe('New Metadata Filter Page', () => { FormBuilder, NgbPopoverConfig, NavigatorService, - SchemaService, - { provide: WidgetRegistry, useClass: DefaultWidgetRegistry } + SchemaService ], imports: [ StoreModule.forRoot({ @@ -48,7 +48,7 @@ describe('New Metadata Filter Page', () => { instance = fixture.componentInstance; store = TestBed.get(Store); - spyOn(store, 'dispatch').and.callThrough(); + spyOn(store, 'dispatch'); }); it('should compile', () => { @@ -66,9 +66,10 @@ describe('New Metadata Filter Page', () => { }); describe('preview method', () => { - it('should dispatch a cancel changes action', () => { + it('should dispatch a preview action', () => { fixture.detectChanges(); - instance.cancel(); + instance.definition = MetadataFilterTypes.EntityAttributes; + instance.preview('foo'); expect(store.dispatch).toHaveBeenCalled(); }); }); @@ -77,21 +78,18 @@ describe('New Metadata Filter Page', () => { it('should set the isValid property to true', () => { fixture.detectChanges(); instance.statusChangeSubject.next({value: []}); - fixture.detectChanges(); expect(instance.isValid).toBe(true); }); it('should set the isValid property to true if value is undefined', () => { fixture.detectChanges(); instance.statusChangeSubject.next({value: null}); - fixture.detectChanges(); expect(instance.isValid).toBe(true); }); it('should set the isValid property to false', () => { fixture.detectChanges(); instance.statusChangeSubject.next({ value: [{control: 'foo'}] }); - fixture.detectChanges(); expect(instance.isValid).toBe(false); }); }); diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index 21983f5a9..5143f7dbe 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -11,7 +11,7 @@ import { UpdateFilterRequest } from '../action/collection.action'; import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; import { EntityAttributesFilterEntity } from '../../domain/entity'; -import { shareReplay, map, withLatestFrom } from 'rxjs/operators'; +import { shareReplay, map, withLatestFrom, filter, switchMap, startWith, defaultIfEmpty } from 'rxjs/operators'; @Component({ selector: 'edit-filter-page', @@ -25,6 +25,7 @@ export class EditFilterComponent { statusChangeSubject = new Subject<{ value: any[] }>(); private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); + definition$: Observable>; definition: FormDefinition; schema$: Observable; @@ -32,6 +33,7 @@ export class EditFilterComponent { isSaving$: Observable; filter: MetadataFilter; isValid: boolean; + type$: Observable; validators$: Observable<{ [key: string]: any }>; @@ -41,11 +43,23 @@ export class EditFilterComponent { private store: Store, private schemaService: SchemaService ) { - this.definition = MetadataFilterTypes.EntityAttributesFilter; + this.definition$ = this.store.select(fromFilter.getFilterType).pipe( + filter(t => !!t), + map(t => MetadataFilterTypes[t]) + ); + + this.definition$.subscribe(d => this.definition = d); - this.schema$ = this.schemaService.get(this.definition.schema).pipe(shareReplay()); + this.schema$ = this.definition$.pipe( + filter(d => !!d), + switchMap(d => { + return this.schemaService.get(d.schema); + }), + shareReplay() + ); this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); + this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); this.valueChangeEmitted$.subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); this.statusChangeEmitted$.subscribe(valid => { @@ -54,9 +68,10 @@ export class EditFilterComponent { this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( withLatestFrom( - this.store.select(fromFilter.getSelectedFilter) + this.store.select(fromFilter.getSelectedFilter), + this.definition$ ), - map(([names, provider]) => this.definition.getValidators( + map(([names, provider, definition]) => definition.getValidators( names.filter(n => n !== provider.name) )) ); @@ -83,7 +98,7 @@ export class EditFilterComponent { preview(id: string): void { this.store.dispatch(new PreviewEntity({ id, - entity: new EntityAttributesFilterEntity(this.filter) + entity: this.definition.getEntity(this.filter) })); } } diff --git a/ui/src/app/metadata/filter/container/new-filter.component.html b/ui/src/app/metadata/filter/container/new-filter.component.html index 08633e938..47f622e3b 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.html +++ b/ui/src/app/metadata/filter/container/new-filter.component.html @@ -10,27 +10,48 @@ -
-
- -   - +
+
+
+
+
+ + +
+
+
+ + +   + + +
+
+
+ +
-
-
+
\ No newline at end of file diff --git a/ui/src/app/metadata/filter/container/new-filter.component.ts b/ui/src/app/metadata/filter/container/new-filter.component.ts index 043936fc2..7fa2a5c7f 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.ts +++ b/ui/src/app/metadata/filter/container/new-filter.component.ts @@ -1,7 +1,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; +import { Validators, FormBuilder, FormGroup } from '@angular/forms'; import { Subject, Observable, of } from 'rxjs'; -import { takeUntil, shareReplay, withLatestFrom, map, filter } from 'rxjs/operators'; + +import { takeUntil, shareReplay, withLatestFrom, map, switchMap, filter, startWith, distinctUntilChanged, share } from 'rxjs/operators'; + import * as fromFilter from '../reducer'; import { MetadataFilterTypes } from '../model'; @@ -9,7 +12,8 @@ import { FormDefinition } from '../../../wizard/model'; import { MetadataFilter } from '../../domain/model'; import { SchemaService } from '../../../schema-form/service/schema.service'; import { AddFilterRequest } from '../action/collection.action'; -import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; +import { CancelCreateFilter, UpdateFilterChanges, SelectFilterType } from '../action/filter.action'; + @Component({ selector: 'new-filter-page', @@ -25,29 +29,56 @@ export class NewFilterComponent implements OnDestroy, OnInit { statusChangeSubject = new Subject<{ value: any[] }>(); private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); - definition: FormDefinition; + definition$: Observable>; schema$: Observable; - model$: Observable; + changes$: Observable; + model: any; isSaving$: Observable; filter: MetadataFilter; isValid: boolean; validators$: Observable<{ [key: string]: any }>; + form: FormGroup = this.fb.group({ + type: ['', Validators.required] + }); + + options$: Observable[]>; + constructor( private store: Store, - private schemaService: SchemaService + private schemaService: SchemaService, + private fb: FormBuilder ) { - this.definition = MetadataFilterTypes.EntityAttributesFilter; - - this.schema$ = this.schemaService.get(this.definition.schema).pipe(shareReplay()); this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); - this.model$ = of({}); - this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( - map((names) => this.definition.getValidators(names)) + this.changes$ = this.store.select(fromFilter.getFilter); + + this.model = {}; + + this.definition$ = this.store.select(fromFilter.getFilterType).pipe( + filter(t => !!t), + map(t => MetadataFilterTypes[t]) + ); + this.schema$ = this.definition$.pipe( + filter(d => !!d), + switchMap(d => { + return this.schemaService.get(d.schema); + }), + shareReplay() + ); + + this.validators$ = this.definition$.pipe( + withLatestFrom(this.store.select(fromFilter.getFilterNames)), + map(([definition, names]) => definition.getValidators(names)) ); + + this.options$ = of(Object.values(MetadataFilterTypes)); + + this.form.get('type').valueChanges + .pipe(distinctUntilChanged()) + .subscribe(type => this.store.dispatch(new SelectFilterType(type))); } ngOnInit(): void { @@ -62,7 +93,11 @@ export class NewFilterComponent implements OnDestroy, OnInit { this.store .select(fromFilter.getFilter) - .pipe(takeUntil(this.ngUnsubscribe)) + .pipe( + takeUntil(this.ngUnsubscribe), + withLatestFrom(this.definition$), + map(([filter, definition]) => definition.parser(filter)) + ) .subscribe(filter => this.filter = filter); } @@ -72,6 +107,7 @@ export class NewFilterComponent implements OnDestroy, OnInit { } save(): void { + console.log(this.filter); this.store.dispatch(new AddFilterRequest(this.filter)); } diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index e04265eb2..65d5f8d2c 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -39,6 +39,7 @@ import { removeNulls, array_move } from '../../../shared/util'; import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; import { MetadataFilterService } from '../../domain/service/filter.service'; import { SelectProviderRequest } from '../../provider/action/collection.action'; +import { UpdateFilterChanges } from '../action/filter.action'; /* istanbul ignore next */ @Injectable() @@ -74,16 +75,17 @@ export class FilterCollectionEffects { ) ); + @Effect() + selectFilterRequestSetChanges$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.SELECT_FILTER_SUCCESS), + map(action => action.payload), + map(filter => new UpdateFilterChanges({...filter, type: filter['@type']})) + ); + @Effect() addFilter$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.ADD_FILTER_REQUEST), map(action => action.payload), - map(filter => { - return { - ...filter, - relyingPartyOverrides: removeNulls(new EntityAttributesFilterEntity(filter).relyingPartyOverrides) - }; - }), withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), switchMap(([unsaved, providerId]) => { return this.filterService diff --git a/ui/src/app/metadata/filter/model/entity-attributes.filter.spec.ts b/ui/src/app/metadata/filter/model/entity-attributes.filter.spec.ts index f21b2db4b..53e3630c8 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes.filter.spec.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes.filter.spec.ts @@ -35,9 +35,9 @@ describe('Entity Attributes filter form', () => { }); describe('transformer', () => { - it('should not modify the object', () => { + it('should add modify the object', () => { expect(EntityAttributesFilter.formatter({})).toEqual({}); - expect(EntityAttributesFilter.parser({})).toEqual({}); + expect(EntityAttributesFilter.parser({}).relyingPartyOverrides).toBeDefined(); }); }); }); diff --git a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts index 5c40ecfb1..78ee24e43 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts @@ -1,10 +1,15 @@ import { FormDefinition } from '../../../wizard/model'; import { MetadataFilter } from '../../domain/model'; +import { removeNulls } from '../../../shared/util'; +import { EntityAttributesFilterEntity } from '../../domain/entity'; export const EntityAttributesFilter: FormDefinition = { label: 'EntityAttributes', type: 'EntityAttributes', schema: '/api/ui/EntityAttributesFilters', + getEntity(filter: MetadataFilter): EntityAttributesFilterEntity { + return new EntityAttributesFilterEntity(filter); + }, getValidators(namesList: string[] = []): any { const validators = { '/': (value, property, form_current) => { @@ -23,7 +28,6 @@ export const EntityAttributesFilter: FormDefinition = { return errors; }, '/name': (value, property, form) => { - console.log(namesList); const err = namesList.indexOf(value) > -1 ? { code: 'INVALID_NAME', path: `#${property.path}`, @@ -35,6 +39,11 @@ export const EntityAttributesFilter: FormDefinition = { }; return validators; }, - parser: (changes: any): MetadataFilter => changes, + parser: (changes: any): MetadataFilter => { + return { + ...changes, + relyingPartyOverrides: removeNulls(new EntityAttributesFilterEntity(changes).relyingPartyOverrides) + }; + }, formatter: (changes: MetadataFilter): any => changes }; diff --git a/ui/src/app/metadata/filter/model/index.ts b/ui/src/app/metadata/filter/model/index.ts index c77b3a90f..7c2c205b3 100644 --- a/ui/src/app/metadata/filter/model/index.ts +++ b/ui/src/app/metadata/filter/model/index.ts @@ -1,7 +1,10 @@ import { EntityAttributesFilter } from './entity-attributes.filter'; +import { NameIDFilter } from './nameid.filter'; export const MetadataFilterTypes = { - EntityAttributesFilter + EntityAttributes: EntityAttributesFilter, + NameIDFormat: NameIDFilter }; export * from './entity-attributes.filter'; +export * from './nameid.filter'; diff --git a/ui/src/app/metadata/filter/model/nameid.filter.spec.ts b/ui/src/app/metadata/filter/model/nameid.filter.spec.ts new file mode 100644 index 000000000..f366ca90a --- /dev/null +++ b/ui/src/app/metadata/filter/model/nameid.filter.spec.ts @@ -0,0 +1,43 @@ +import { NameIDFilter } from './nameid.filter'; + +describe('NameID Format filter form', () => { + describe('getValidators', () => { + it('should return an empty object for validators', () => { + expect(Object.keys(NameIDFilter.getValidators())).toEqual([ + '/', + '/name' + ]); + }); + + describe('name `/name` validator', () => { + const validators = NameIDFilter.getValidators(['foo', 'bar']); + + it('should return an invalid object when provided values are invalid based on name', () => { + expect(validators['/name']('foo', { path: '/name' })).toBeDefined(); + }); + + it('should return null when provided values are valid based on name', () => { + expect(validators['/name']('baz', { path: '/name' })).toBeNull(); + }); + }); + + describe('parent `/` validator', () => { + const validators = NameIDFilter.getValidators(['foo', 'bar']); + + it('should return a list of child errors', () => { + expect(validators['/']({ name: 'foo' }, { path: '/name' }, {}).length).toBe(1); + }); + + it('should ignore properties that don\'t exist a list of child errors', () => { + expect(validators['/']({ foo: 'bar' }, { path: '/foo' }, {})).toBeUndefined(); + }); + }); + }); + + describe('transformer', () => { + it('should add modify the object', () => { + expect(NameIDFilter.formatter({})).toEqual({}); + expect(NameIDFilter.parser({})).toEqual({}); + }); + }); +}); diff --git a/ui/src/app/metadata/filter/model/nameid.filter.ts b/ui/src/app/metadata/filter/model/nameid.filter.ts new file mode 100644 index 000000000..3787af80b --- /dev/null +++ b/ui/src/app/metadata/filter/model/nameid.filter.ts @@ -0,0 +1,43 @@ +import { FormDefinition } from '../../../wizard/model'; +import { MetadataFilter } from '../../domain/model'; +import { NameIDFormatFilterEntity } from '../../domain/entity/filter/nameid-format-filter'; + +export const NameIDFilter: FormDefinition = { + label: 'NameIDFilter', + type: 'NameIDFormat', + schema: '/api/ui/NameIdFormatFilter', + getEntity(filter: MetadataFilter): NameIDFormatFilterEntity { + return new NameIDFormatFilterEntity(filter); + }, + getValidators(namesList: string[] = []): any { + const validators = { + '/': (value, property, form_current) => { + let errors; + // iterate all customer + Object.keys(value).forEach((key) => { + const item = value[key]; + const validatorKey = `/${key}`; + const validator = validators.hasOwnProperty(validatorKey) ? validators[validatorKey] : null; + const error = validator ? validator(item, { path: `/${key}` }, form_current) : null; + if (error) { + errors = errors || []; + errors.push(error); + } + }); + return errors; + }, + '/name': (value, property, form) => { + const err = namesList.indexOf(value) > -1 ? { + code: 'INVALID_NAME', + path: `#${property.path}`, + message: 'message.name-must-be-unique', + params: [value] + } : null; + return err; + } + }; + return validators; + }, + parser: (changes: any): MetadataFilter => changes, + formatter: (changes: MetadataFilter): any => changes +}; diff --git a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts index bd7e71514..229478811 100644 --- a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts @@ -1,22 +1,8 @@ import { reducer, initialState as snapshot } from './filter.reducer'; import * as fromFilter from './filter.reducer'; import { SelectId, LoadEntityPreviewSuccess, UpdateFilterChanges, FilterActionTypes, CancelCreateFilter } from '../action/filter.action'; -import { SearchActionTypes } from '../action/search.action'; - -import { - ClearSearch -} from '../action/search.action'; - -import { - FilterCollectionActionTypes, - AddFilterRequest, - UpdateFilterRequest, - AddFilterSuccess, - UpdateFilterSuccess -} from '../action/collection.action'; import { MDUI } from '../../domain/model'; import { MetadataFilter } from '../../domain/model'; -import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; const mdui: MDUI = { displayName: 'foo', @@ -61,26 +47,6 @@ describe('Filter Reducer', () => { expect(result.changes.filterEnabled).toBe(false); }); }); - - describe(`${FilterCollectionActionTypes.ADD_FILTER_SUCCESS} action`, () => { - it('should set saving to true', () => { - const result = reducer(snapshot, new AddFilterSuccess(new EntityAttributesFilterEntity())); - expect(result).toEqual(fromFilter.initialState); - }); - }); - describe(`${FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS} action`, () => { - it('should set saving to true', () => { - const update = {id: 'foo', changes: new EntityAttributesFilterEntity({id: 'foo'})}; - const result = reducer(snapshot, new UpdateFilterSuccess(update)); - expect(result).toEqual(fromFilter.initialState); - }); - }); - describe(`${SearchActionTypes.CLEAR_SEARCH} action`, () => { - it('should set saving to true', () => { - const result = reducer(snapshot, new ClearSearch()); - expect(result).toEqual(fromFilter.initialState); - }); - }); describe(`${FilterActionTypes.CANCEL_CREATE_FILTER} action`, () => { it('should set saving to true', () => { const result = reducer(snapshot, new CancelCreateFilter()); diff --git a/ui/src/app/metadata/filter/reducer/filter.reducer.ts b/ui/src/app/metadata/filter/reducer/filter.reducer.ts index 653d53740..9b47e8df0 100644 --- a/ui/src/app/metadata/filter/reducer/filter.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/filter.reducer.ts @@ -2,11 +2,6 @@ import { FilterActionTypes, FilterActionsUnion } from '../action/filter.action'; -import { - SearchActionTypes, - SearchActionsUnion -} from '../action/search.action'; -import { FilterCollectionActionTypes, FilterCollectionActionsUnion } from '../action/collection.action'; import { MetadataFilter, MDUI } from '../../domain/model'; export interface FilterState { @@ -21,7 +16,7 @@ export const initialState: FilterState = { preview: null }; -export function reducer(state = initialState, action: FilterActionsUnion | SearchActionsUnion | FilterCollectionActionsUnion): FilterState { +export function reducer(state = initialState, action: FilterActionsUnion): FilterState { switch (action.type) { case FilterActionTypes.SELECT_ID: { return { @@ -29,6 +24,14 @@ export function reducer(state = initialState, action: FilterActionsUnion | Searc selected: action.payload }; } + case FilterActionTypes.SELECT_FILTER_TYPE: { + return { + ...state, + changes: { + type: action.payload + } + }; + } case FilterActionTypes.LOAD_ENTITY_PREVIEW_SUCCESS: { return { ...state, diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index 495bf1e52..36682ad4d 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -79,5 +79,11 @@ export const getFilterNames = createSelector(getAllFilters, (filters: MetadataFi */ export const mergeFn = (changes, filter) => ({ ...filter, ...changes }); +export const detectFilterType = (changes) => changes.type ? changes.type : changes.hasOwnProperty('@type') ? changes['@type'] : null; export const getFilterWithChanges = createSelector(getFilter, getSelectedFilter, mergeFn); +export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => { + const type = changes ? detectFilterType(changes) : null; + return type; +}); + diff --git a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts index f778451da..4b18f91f4 100644 --- a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts @@ -90,6 +90,13 @@ describe('Filter Reducer', () => { }); }); + describe(`${SearchActionTypes.CLEAR_SEARCH} action`, () => { + it('should set saving to true', () => { + const result = reducer(snapshot, new ClearSearch()); + expect(result).toEqual(fromFilter.initialState); + }); + }); + describe('selector methods', () => { describe('getViewMore', () => { it('should return the state viewMore', () => { diff --git a/ui/src/app/schema-form/registry.ts b/ui/src/app/schema-form/registry.ts index f372e88f6..266aeb9a2 100644 --- a/ui/src/app/schema-form/registry.ts +++ b/ui/src/app/schema-form/registry.ts @@ -2,11 +2,10 @@ import { BooleanRadioComponent } from './widget/boolean-radio/boolean-radio.comp import { FieldsetComponent } from './widget/fieldset/fieldset.component'; import { CustomStringComponent } from './widget/string/string.component'; -import { WidgetRegistry, ObjectWidget } from 'ngx-schema-form'; +import { WidgetRegistry } from 'ngx-schema-form'; import { ButtonWidget } from 'ngx-schema-form'; import { FileWidget } from 'ngx-schema-form'; -import { RadioWidget } from 'ngx-schema-form'; import { RangeWidget } from 'ngx-schema-form'; import { CustomSelectComponent } from './widget/select/select.component'; import { DatalistComponent } from './widget/datalist/datalist.component'; @@ -19,6 +18,8 @@ import { ChecklistComponent } from './widget/check/checklist.component'; import { IconButtonComponent } from './widget/button/icon-button.component'; import { CustomObjectWidget } from './widget/object/object.component'; import { CustomRadioComponent } from './widget/radio/radio.component'; +import { InlineObjectListComponent } from './widget/array/inline-obj-list.component'; +import { InlineObjectComponent } from './widget/object/inline-obj.component'; export class CustomWidgetRegistry extends WidgetRegistry { constructor() { @@ -40,6 +41,8 @@ export class CustomWidgetRegistry extends WidgetRegistry { this.register('fieldset', FieldsetComponent); this.register('object', CustomObjectWidget); + this.register('inline-obj-list', InlineObjectListComponent); + this.register('inline-obj', InlineObjectComponent); this.register('array', CustomArrayComponent); this.register('checklist', ChecklistComponent); diff --git a/ui/src/app/schema-form/schema-form.module.ts b/ui/src/app/schema-form/schema-form.module.ts index 8927b9876..842e903dd 100644 --- a/ui/src/app/schema-form/schema-form.module.ts +++ b/ui/src/app/schema-form/schema-form.module.ts @@ -21,6 +21,8 @@ import { IconButtonComponent } from './widget/button/icon-button.component'; import { I18nModule } from '../i18n/i18n.module'; import { CustomObjectWidget } from './widget/object/object.component'; import { CustomRadioComponent } from './widget/radio/radio.component'; +import { InlineObjectListComponent } from './widget/array/inline-obj-list.component'; +import { InlineObjectComponent } from './widget/object/inline-obj.component'; export const COMPONENTS = [ BooleanRadioComponent, @@ -36,7 +38,9 @@ export const COMPONENTS = [ ChecklistComponent, IconButtonComponent, CustomRadioComponent, - CustomObjectWidget + CustomObjectWidget, + InlineObjectListComponent, + InlineObjectComponent ]; @NgModule({ diff --git a/ui/src/app/schema-form/widget/array/array.component.ts b/ui/src/app/schema-form/widget/array/array.component.ts index 520915baa..70938da88 100644 --- a/ui/src/app/schema-form/widget/array/array.component.ts +++ b/ui/src/app/schema-form/widget/array/array.component.ts @@ -41,6 +41,12 @@ export class CustomArrayComponent extends ArrayWidget implements AfterViewInit, ngOnDestroy(): void { this.hasErrorSub.unsubscribe(); } + + addItem(): void { + super.addItem(); + console.log(this.formProperty.schemaValidator(this.schema)); + } + getListType(property: any): string { return property.properties.length ? property.properties[0].type : null; } diff --git a/ui/src/app/schema-form/widget/array/inline-obj-list.component.html b/ui/src/app/schema-form/widget/array/inline-obj-list.component.html new file mode 100644 index 000000000..876ea01f7 --- /dev/null +++ b/ui/src/app/schema-form/widget/array/inline-obj-list.component.html @@ -0,0 +1,54 @@ +
+
+ +    + +   + + + + + + , + {{ error.message }} + + +
+
    +
  • +
    +
    +
    +
    + + +   + + + + (default) + + + +
    +
    +
    + +
    +
    +
    +
    +
  • +
+ + {{ schema.description }} + +
\ No newline at end of file diff --git a/ui/src/app/schema-form/widget/array/inline-obj-list.component.ts b/ui/src/app/schema-form/widget/array/inline-obj-list.component.ts new file mode 100644 index 000000000..31a15068a --- /dev/null +++ b/ui/src/app/schema-form/widget/array/inline-obj-list.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +import { ObjectWidget } from 'ngx-schema-form'; +import { CustomArrayComponent } from './array.component'; + +/* tslint:disable */ +@Component({ + selector: 'inline-obj-list', + templateUrl: `./inline-obj-list.component.html` +}) +export class InlineObjectListComponent extends CustomArrayComponent { } diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html index 50de6be03..43ff94a15 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html @@ -1,4 +1,4 @@ -
+
@@ -39,14 +39,14 @@    + *ngIf="targetType !== 'ENTITY'">
-
+
-
+
-
+

    diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts index 45e632e9e..4107ebfd3 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts @@ -50,7 +50,7 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af .valueChanges .pipe( distinctUntilChanged(), - skipWhile(() => this.entityAttributesFilterTargetType === 'ENTITY') + skipWhile(() => this.targetType === 'ENTITY') ) .subscribe(script => { this.setTargetValue([script]); @@ -92,24 +92,31 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af return this.formProperty.getProperty('value').value; } - get entityAttributesFilterTargetType(): string { - return this.formProperty.getProperty('entityAttributesFilterTargetType').value; + get targetType(): string { + return this.formProperty.getProperty(this.targetAttribute).value; } get displayType(): string { - return this.typeOptions.find(opt => opt.value === this.entityAttributesFilterTargetType).description; + if (!this.targetAttribute) { + return null; + } + return this.typeOptions.find(opt => opt.value === this.targetType).description; + } + + get targetAttribute(): string { + return this.formProperty.schema.widget.target; } get typeOptions(): any[] { return this.formProperty - .getProperty('entityAttributesFilterTargetType') + .getProperty(this.targetAttribute) .schema .oneOf .map(option => ({ ...option, value: option.enum[0] })); } select(value: string): void { - this.formProperty.getProperty('entityAttributesFilterTargetType').setValue(value); + this.formProperty.getProperty(this.targetAttribute).setValue(value); this.setTargetValue([]); this.script.reset(); this.search.reset(); diff --git a/ui/src/app/schema-form/widget/object/inline-obj.component.html b/ui/src/app/schema-form/widget/object/inline-obj.component.html new file mode 100644 index 000000000..a9f57749f --- /dev/null +++ b/ui/src/app/schema-form/widget/object/inline-obj.component.html @@ -0,0 +1,9 @@ +
    + +
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/ui/src/app/schema-form/widget/object/inline-obj.component.ts b/ui/src/app/schema-form/widget/object/inline-obj.component.ts new file mode 100644 index 000000000..4ab29534a --- /dev/null +++ b/ui/src/app/schema-form/widget/object/inline-obj.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +import { CustomObjectWidget } from './object.component'; + +/* tslint:disable */ +@Component({ + selector: 'inline-obj', + templateUrl: `./inline-obj.component.html` +}) +export class InlineObjectComponent extends CustomObjectWidget { } diff --git a/ui/src/app/wizard/model/form-definition.ts b/ui/src/app/wizard/model/form-definition.ts index f7036014d..997937961 100644 --- a/ui/src/app/wizard/model/form-definition.ts +++ b/ui/src/app/wizard/model/form-definition.ts @@ -3,6 +3,7 @@ export interface FormDefinition { type: string; schema?: string; bindings?: any; + getEntity?(entity: any): any; parser(changes: Partial, schema?: any); formatter(changes: Partial, schema?: any); getValidators?(...args: any[]): { [key: string]: any }; diff --git a/ui/src/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json new file mode 100644 index 000000000..2ddb21915 --- /dev/null +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -0,0 +1,149 @@ +{ + "type": "object", + "fieldsets": [ + { + "type": "group-lg", + "fields": [ + "name", + "nameIdFormatFilterTarget", + "filterEnabled", + "@type", + "resourceId", + "version", + "removeExistingFormats" + ] + }, + { + "type": "group", + "fields": [ + "formats" + ] + } + ], + "required": [ + "name" + ], + "properties": { + "name": { + "title": "label.filter-name", + "description": "tooltip.filter-name", + "type": "string", + "widget": { + "id": "string", + "help": "message.must-be-unique" + } + }, + "filterEnabled": { + "title": "label.enable-filter", + "description": "tooltip.enable-filter", + "type": "boolean", + "default": false + }, + "nameIdFormatFilterTarget": { + "title": "label.search-criteria", + "description": "tooltip.search-criteria", + "type": "object", + "widget": { + "id": "filter-target", + "target": "nameIdFormatFilterTargetType" + }, + "properties": { + "nameIdFormatFilterTargetType": { + "title": "", + "type": "string", + "default": "ENTITY", + "oneOf": [ + { + "enum": [ + "ENTITY" + ], + "description": "value.entity-id" + }, + { + "enum": [ + "REGEX" + ], + "description": "value.regex" + }, + { + "enum": [ + "CONDITION_SCRIPT" + ], + "description": "value.script" + } + ] + }, + "value": { + "type": "array", + "buttons": [ + { + "id": "preview", + "label": "action.preview", + "widget": "icon-button" + } + ], + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string" + } + } + }, + "required": [ + "value", + "nameIdFormatFilterTargetType" + ] + }, + "@type": { + "type": "string", + "widget": { + "id": "hidden" + }, + "default": "NameIDFormat" + }, + "version": { + "type": "integer", + "widget": { + "id": "hidden" + } + }, + "resourceId": { + "type": "string", + "widget": { + "id": "hidden" + } + }, + "removeExistingFormats": { + "type": "boolean", + "title": "label.remove-existing-formats", + "description": "tooltip.remove-existing-formats", + "default": false + }, + "formats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "definitions": { + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 255, + "widget": { + "id": "datalist", + "data": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + ] + } + } + } + } +} \ No newline at end of file