From 584630fbe3d14fb817cde32ff55b5827abed89b0 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 26 Nov 2018 14:20:58 -0500 Subject: [PATCH 01/38] SHIBUI-979 WIP --- .../ui/domain/filters/MetadataFilter.java | 3 ++- .../ui/domain/filters/NameIdFormatFilter.java | 20 +++++++++++++++++++ ...ymorphicFiltersJacksonHandlingTests.groovy | 1 + .../admin/ui/util/TestObjectGenerator.groovy | 8 ++++++++ settings.gradle | 2 +- 5 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java 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..4c15f3040 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java @@ -0,0 +1,20 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +@ToString +public class NameIdFormatFilter extends MetadataFilter { + + public NameIdFormatFilter() { + type = "NameIDFormat"; + } +} 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..e5aa4380d 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') } 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 eaf031c3e..f726bde01 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 @@ -130,6 +130,7 @@ class TestObjectGenerator { filterList.add(buildFilter { entityRoleWhitelistFilter() }) filterList.add(buildFilter { signatureValidationFilter() }) filterList.add(buildFilter { requiredValidUntilFilter() }) + filterList.add(buildFilter { nameIdFormatFilter() }) } return filterList } @@ -212,6 +213,13 @@ class TestObjectGenerator { } } + NameIdFormatFilter nameIdFormatFilter() { + return new NameIdFormatFilter().with { + it.name = "NameIDFormat" + it + } + } + RequiredValidUntilFilter copyOf(RequiredValidUntilFilter requiredValidUntilFilter) { new RequiredValidUntilFilter().with { it.name = requiredValidUntilFilter.name diff --git a/settings.gradle b/settings.gradle index 8fae26617..7fe93709f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include 'backend', 'ui', 'pac4j-module' \ No newline at end of file +include 'backend', 'ui', 'pacj-module' \ No newline at end of file From 3a55e5a45bdf5ecd6e43e7f65274907a104bfd4c Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 26 Nov 2018 16:15:57 -0500 Subject: [PATCH 02/38] SHIBUI-979 WIP --- .../ui/domain/filters/NameIdFormatFilter.java | 28 ++++++++++++++ ...ymorphicFiltersJacksonHandlingTests.groovy | 38 +++++++++++++++++++ .../repository/FilterRepositoryTests.groovy | 15 ++++++++ .../admin/ui/util/TestObjectGenerator.groovy | 25 ++++++++++-- 4 files changed, 102 insertions(+), 4 deletions(-) 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 index 4c15f3040..0817d338c 100644 --- 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 @@ -1,11 +1,17 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; import javax.persistence.Entity; +import javax.persistence.OrderColumn; +import java.util.List; @Entity @EqualsAndHashCode(callSuper = true) @@ -17,4 +23,26 @@ public class NameIdFormatFilter extends MetadataFilter { public NameIdFormatFilter() { type = "NameIDFormat"; } + + private Boolean removeExistingFormats = false; + + @ElementCollection + @OrderColumn + private List formats; + + @Embeddable + @NoArgsConstructor + @AllArgsConstructor + @Getter + @Setter + @EqualsAndHashCode + public static class FormatAndTarget { + private String format; + private String value; + private Type type; + + private enum Type { + ENTITY, CONDITION_REF, CONDITION_SCRIPT + } + } } 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 e5aa4380d..adff06db3 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 @@ -175,4 +175,42 @@ 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 = """ + { + "@type" : "NameIDFormat", + "name" : "NameIDFormat", + "resourceId" : "20147f53-e368-4911-921a-2e24598e37f8", + "filterEnabled" : true, + "removeExistingFormats" : false, + "formats" : [ { + "format" : "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "value" : "https://sp1.example.org", + "type" : "ENTITY" + }, { + "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "value" : "https://sp2.example.org", + "type" : "ENTITY" + }, { + "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + "value" : "input.getEntityID().equals(\\"https://sp1.example.org\\");", + "type" : "CONDITION_SCRIPT" + } ] + }""" + + 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..5d9829ad9 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() == 3 + } } 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 f726bde01..f07dd212b 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,11 @@ 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.NameIdFormatFilter.* +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.* +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.* +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.* + /** * @author Bill Smith (wsmith@unicon.net) */ @@ -169,6 +174,7 @@ class TestObjectGenerator { it } } + EntityRoleWhiteListFilter entityRoleWhitelistFilter() { new EntityRoleWhiteListFilter().with { it.name = 'EntityRoleWhiteList' @@ -213,9 +219,20 @@ class TestObjectGenerator { } } - NameIdFormatFilter nameIdFormatFilter() { + static NameIdFormatFilter nameIdFormatFilter() { return new NameIdFormatFilter().with { it.name = "NameIDFormat" + it.formats = [ + new FormatAndTarget( + format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', + type: Type.ENTITY, value: 'https://sp1.example.org'), + new FormatAndTarget( + format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + type: Type.ENTITY, value: 'https://sp2.example.org'), + new FormatAndTarget( + format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + type: Type.CONDITION_SCRIPT, value: 'input.getEntityID().equals("https://sp1.example.org");') + ] it } } @@ -276,12 +293,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())) @@ -326,7 +343,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()) From b499bb99d9d14e2309b71e2482b93d6d2638ab19 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 27 Nov 2018 11:58:24 -0500 Subject: [PATCH 03/38] SHIBUI-980 wip --- .../ui/controller/MetadataFiltersController.java | 7 +++++++ .../MetadataFiltersControllerTests.groovy | 2 ++ ...PolymorphicFiltersJacksonHandlingTests.groovy | 4 ++++ .../ui/repository/FilterRepositoryTests.groovy | 2 +- .../admin/ui/util/TestObjectGenerator.groovy | 16 ++++++++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) 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..0ae56b781 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,12 @@ 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()); + } //TODO: add other types of concrete filters update here } 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..15717ca61 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 @@ -177,6 +177,7 @@ class MetadataFiltersControllerTests extends Specification { 'entityRoleWhiteList' | _ 'signatureValidation' | _ 'requiredValidUntil' | _ + 'nameIdFormat' | _ } @Unroll @@ -222,6 +223,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 adff06db3..383632119 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 @@ -193,6 +193,10 @@ class PolymorphicFiltersJacksonHandlingTests extends Specification { "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "value" : "https://sp2.example.org", "type" : "ENTITY" + }, { + "format" : "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "value" : "conditionRefBeanId", + "type" : "CONDITION_REF" }, { "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "value" : "input.getEntityID().equals(\\"https://sp1.example.org\\");", 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 5d9829ad9..7a733d011 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 @@ -83,6 +83,6 @@ class FilterRepositoryTests extends Specification { then: persistedFilter.audId > 0L - persistedFilter.formats.size() == 3 + persistedFilter.formats.size() == 4 } } 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 f07dd212b..1a44cf1c7 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 @@ -155,6 +155,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} ?"); } @@ -229,6 +232,9 @@ class TestObjectGenerator { new FormatAndTarget( format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', type: Type.ENTITY, value: 'https://sp2.example.org'), + new FormatAndTarget( + format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:persistent', + type: Type.CONDITION_REF, value: 'conditionRefBeanId'), new FormatAndTarget( format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', type: Type.CONDITION_SCRIPT, value: 'input.getEntityID().equals("https://sp1.example.org");') @@ -283,6 +289,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()) From 07ce4137fa3b860ae95a5cd5c2aaaeb253d329a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 27 Nov 2018 16:38:27 -0500 Subject: [PATCH 04/38] 1016 WIP --- .../JPAMetadataResolverServiceImpl.groovy | 29 +++++++++++++++++++ .../ui/domain/filters/NameIdFormatFilter.java | 2 +- .../repository/FilterRepositoryTests.groovy | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) 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 4a95484e8..ac71a6976 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 @@ -4,6 +4,7 @@ import com.google.common.base.Predicate import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.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.DynamicHttpMetadataResolver @@ -30,6 +31,9 @@ import org.w3c.dom.Document import javax.annotation.Nonnull +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.CONDITION_REF +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.CONDITION_SCRIPT +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.ENTITY 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 @@ -220,6 +224,31 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForFilter(NameIdFormatFilter filter, def markupBuilderDelegate) { + markupBuilderDelegate.MetadataFilter( + 'xsi:type': 'NameIDFormat', + 'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata', + 'removeExistingFormats': filter.removeExistingFormats ?: null + ) { + filter.formats.each { + Format(it.format) + if(it.type == ENTITY) { + Entity(it.value) + } + else if(it.type == CONDITION_REF) { + ConditionRef(it.value) + } + else if(it.type == CONDITION_SCRIPT) { + ConditionScript() { + Script() { + mkp.yieldUnescaped("\n\n") + } + } + } + } + } + } + void constructXmlNodeForResolver(FilesystemMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) { markupBuilderDelegate.MetadataProvider(id: resolver.xmlId, 'xsi:type': 'FilesystemMetadataProvider', 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 index 0817d338c..a2fb316f3 100644 --- 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 @@ -41,7 +41,7 @@ public static class FormatAndTarget { private String value; private Type type; - private enum Type { + public enum Type { ENTITY, CONDITION_REF, CONDITION_SCRIPT } } 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 7a733d011..6396f27ea 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 @@ -83,6 +83,6 @@ class FilterRepositoryTests extends Specification { then: persistedFilter.audId > 0L - persistedFilter.formats.size() == 4 + persistedFilter.formats.size() == 4z } } From 3cbc68585ddb87427ed65acf593416f714a3a2e0 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 28 Nov 2018 10:55:27 -0500 Subject: [PATCH 05/38] SHIBUI-799 Fix test --- .../shibboleth/admin/ui/repository/FilterRepositoryTests.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6396f27ea..7a733d011 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 @@ -83,6 +83,6 @@ class FilterRepositoryTests extends Specification { then: persistedFilter.audId > 0L - persistedFilter.formats.size() == 4z + persistedFilter.formats.size() == 4 } } From 2c340a87ecf59d8f094fd129264fafa7b7c6fde8 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 28 Nov 2018 11:49:21 -0500 Subject: [PATCH 06/38] SHIBUI-799: testing --- .../JPAMetadataResolverServiceImpl.groovy | 3 ++- ...JPAMetadataResolverServiceImplTests.groovy | 11 +++++++++ .../admin/ui/util/TestObjectGenerator.groovy | 6 ++--- backend/src/test/resources/conf/799.xml | 24 +++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 backend/src/test/resources/conf/799.xml 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 ac71a6976..518c3bbf4 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 @@ -239,9 +239,10 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { ConditionRef(it.value) } else if(it.type == CONDITION_SCRIPT) { + def scriptText = it.value ConditionScript() { Script() { - mkp.yieldUnescaped("\n\n") + mkp.yieldUnescaped("\n\n") } } } 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 8bb9bc7d4..47e21c3a9 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 @@ -199,6 +199,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 1a44cf1c7..8fec66063 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 @@ -230,13 +230,13 @@ class TestObjectGenerator { format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', type: Type.ENTITY, value: 'https://sp1.example.org'), new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress', type: Type.ENTITY, value: 'https://sp2.example.org'), new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:persistent', + format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', type: Type.CONDITION_REF, value: 'conditionRefBeanId'), new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', type: Type.CONDITION_SCRIPT, value: 'input.getEntityID().equals("https://sp1.example.org");') ] it diff --git a/backend/src/test/resources/conf/799.xml b/backend/src/test/resources/conf/799.xml new file mode 100644 index 000000000..b7e32082a --- /dev/null +++ b/backend/src/test/resources/conf/799.xml @@ -0,0 +1,24 @@ + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + https://sp1.example.org + urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress + https://sp2.example.org + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + conditionRefBeanId + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + + + From 3b8ccc1560e8985625c9ff5102f2646dc02919aa Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 28 Nov 2018 12:05:02 -0700 Subject: [PATCH 07/38] SHIBUI-799 Initial schema commit --- .../assets/schema/filter/nameid.schema.json | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 ui/src/assets/schema/filter/nameid.schema.json 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..6a14031dd --- /dev/null +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -0,0 +1,102 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "title": "The Root Schema", + "required": [ + "name", + "resourceId", + "filterEnabled", + "removeExistingFormats", + "formats", + "@type", + "version" + ], + "properties": { + "name": { + "$id": "#/properties/name", + "type": "string", + "title": "label.name", + "pattern": "^(.*)$" + }, + "filterEnabled": { + "$id": "#/properties/filterEnabled", + "type": "boolean", + "title": "The Filterenabled Schema" + }, + "removeExistingFormats": { + "$id": "#/properties/removeExistingFormats", + "type": "boolean", + "title": "The Removeexistingformats Schema", + "default": false + }, + "formats": { + "$id": "#/properties/formats", + "type": "array", + "title": "The Formats Schema", + "items": { + "$id": "#/properties/formats/items", + "type": "object", + "title": "The Items Schema", + "required": [ + "format", + "value", + "type" + ], + "properties": { + "format": { + "$id": "#/properties/formats/items/properties/format", + "type": "string", + "title": "The Format Schema", + "pattern": "^(.*)$" + }, + "value": { + "$id": "#/properties/formats/items/properties/value", + "type": "string", + "title": "The Value Schema" + }, + "type": { + "$id": "#/properties/formats/items/properties/type", + "type": "string", + "title": "The Type Schema", + "widget": "select", + "oneOf": [ + { + "enum": [ + "ENTITY" + ], + "description": "value.entity" + }, + { + "enum": [ + "CONDITION_REF" + ], + "description": "value.condition-ref" + }, + { + "enum": [ + "CONDITION_SCRIPT" + ], + "description": "value.condition-script" + } + ] + } + } + } + }, + "@type": { + "$id": "#/properties/@type", + "type": "string", + "title": "The @type Schema", + "pattern": "^(.*)$" + }, + "version": { + "type": "integer", + "widget": "hidden" + }, + "resourceId": { + "type": "string", + "widget": "hidden" + } + } +} \ No newline at end of file From 332504c57d7036c12b4e97e6b58a1202c00a0764 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 29 Nov 2018 14:31:14 -0500 Subject: [PATCH 08/38] SHIBUI-799: SHIBUI-1022 implement predicate rules during filter reload --- .../JPAMetadataResolverServiceImpl.groovy | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) 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 5cdb98f5d..923c88417 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 @@ -28,6 +28,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 @@ -96,6 +97,26 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { target.setRules(rules) metadataFilters.add(target) } + if(metadataFilter instanceof NameIdFormatFilter) { + NameIdFormatFilter nameIdFormatFilter = NameIdFormatFilter.cast(metadataFilter) + NameIDFormatFilter openSamlTargetFilter = new NameIDFormatFilter() + Map, Collection> predicateRules = [:] + nameIdFormatFilter.formats.each { + switch (it.type) { + case ENTITY: + predicateRules.put(new EntityIdPredicate([it.value]), [it.format]) + break + case CONDITION_SCRIPT: + predicateRules.put(new ScriptedPredicate(new EvaluableScript(it.value)), [it.format]) + break + default: + // do nothing, we'd have exploded elsewhere previously. + break + } + } + openSamlTargetFilter.rules = predicateRules + metadataFilters << openSamlTargetFilter + } } metadataFilterChain.setFilters(metadataFilters) } From 132788ececbec483c79855ffdc021f5ce96246bd Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 5 Dec 2018 09:24:40 -0700 Subject: [PATCH 09/38] Implemented NameIDFormat Filter --- .../resources/i18n/messages_en.properties | 17 +- ui/package-lock.json | 3765 ++++++++++++++++- .../metadata/domain/model/metadata-filter.ts | 2 +- .../metadata/filter/action/filter.action.ts | 8 + .../filter/container/edit-filter.component.ts | 2 +- .../container/new-filter.component.html | 59 +- .../filter/container/new-filter.component.ts | 60 +- .../filter/effect/collection.effect.ts | 6 - .../filter/model/entity-attributes.filter.ts | 9 +- ui/src/app/metadata/filter/model/index.ts | 5 +- .../metadata/filter/model/nameid.filter.ts | 14 + .../metadata/filter/reducer/filter.reducer.ts | 15 +- ui/src/app/metadata/filter/reducer/index.ts | 2 + ui/src/app/schema-form/registry.ts | 3 +- .../widget/array/array.component.ts | 6 + .../assets/schema/filter/nameid.schema.json | 103 +- 16 files changed, 3958 insertions(+), 118 deletions(-) create mode 100644 ui/src/app/metadata/filter/model/nameid.filter.ts diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index ca4fa40c6..32b85ae4c 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 @@ -364,6 +369,10 @@ 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 message.must-be-unique=Must be unique. message.name-must-be-unique=Name must be unique. @@ -512,4 +521,10 @@ 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=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.nameid-formats-format=Format +tooltip.nameid-formats-value=Value +tooltip.nameid-formats-type=Type \ No newline at end of file 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/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/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.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index 21983f5a9..c71e7a489 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -41,7 +41,7 @@ export class EditFilterComponent { private store: Store, private schemaService: SchemaService ) { - this.definition = MetadataFilterTypes.EntityAttributesFilter; + this.definition = MetadataFilterTypes.EntityAttributes; this.schema$ = this.schemaService.get(this.definition.schema).pipe(shareReplay()); this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); 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 e56dc4634..6168e374d 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,46 @@ -
-
- -   - +
+
+
+
+
+ + +
+
+
+ + +   + + +
+
+
+ +
-
+
{{ changes$ | async | json }}
+
{{ schema$ | async | json }}
\ 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..08f4d509a 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: [null, 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..8e640d75e 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -78,12 +78,6 @@ export class FilterCollectionEffects { 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.ts b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts index 5c40ecfb1..83daa063d 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts @@ -1,5 +1,7 @@ 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', @@ -35,6 +37,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.ts b/ui/src/app/metadata/filter/model/nameid.filter.ts new file mode 100644 index 000000000..f98a92eae --- /dev/null +++ b/ui/src/app/metadata/filter/model/nameid.filter.ts @@ -0,0 +1,14 @@ +import { FormDefinition } from '../../../wizard/model'; +import { MetadataFilter } from '../../domain/model'; + +export const NameIDFilter: FormDefinition = { + label: 'NameIDFilter', + type: 'NameIDFormat', + schema: 'assets/schema/filter/nameid.schema.json', + getValidators(): any { + const validators = {}; + return validators; + }, + parser: (changes: any): MetadataFilter => changes, + formatter: (changes: MetadataFilter): any => changes +}; 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..fdf577027 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -81,3 +81,5 @@ export const getFilterNames = createSelector(getAllFilters, (filters: MetadataFi export const mergeFn = (changes, filter) => ({ ...filter, ...changes }); export const getFilterWithChanges = createSelector(getFilter, getSelectedFilter, mergeFn); +export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => changes ? changes.type : null); + diff --git a/ui/src/app/schema-form/registry.ts b/ui/src/app/schema-form/registry.ts index f372e88f6..f1b6149a2 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'; 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/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index 6a14031dd..b52ee8ceb 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -1,65 +1,84 @@ { - "definitions": {}, - "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "title": "The Root Schema", - "required": [ - "name", - "resourceId", - "filterEnabled", - "removeExistingFormats", - "formats", - "@type", - "version" + "fieldsets": [ + { + "type": "group", + "fields": [ + "name", + "filterEnabled", + "@type", + "resourceId", + "version", + "removeExistingFormats", + "formats" + ] + } ], "properties": { "name": { - "$id": "#/properties/name", + "title": "label.filter-name", + "description": "tooltip.filter-name", "type": "string", - "title": "label.name", - "pattern": "^(.*)$" + "widget": { + "id": "string", + "help": "message.must-be-unique" + } }, "filterEnabled": { - "$id": "#/properties/filterEnabled", + "title": "label.enable-filter", + "description": "tooltip.enable-filter", "type": "boolean", - "title": "The Filterenabled Schema" + "default": false + }, + "@type": { + "type": "string", + "widget": { + "id": "hidden" + }, + "default": "NameIDFormat" + }, + "version": { + "type": "integer", + "widget": { + "id": "hidden" + } + }, + "resourceId": { + "type": "string", + "widget": { + "id": "hidden" + } }, "removeExistingFormats": { - "$id": "#/properties/removeExistingFormats", "type": "boolean", - "title": "The Removeexistingformats Schema", + "title": "label.remove-existing-formats", + "description": "tooltip.remove-existing-formats", "default": false }, "formats": { - "$id": "#/properties/formats", "type": "array", - "title": "The Formats Schema", + "title": "label.nameid-formats", + "description": "tooltip.nameid-formats", "items": { - "$id": "#/properties/formats/items", "type": "object", - "title": "The Items Schema", - "required": [ - "format", - "value", - "type" - ], "properties": { "format": { - "$id": "#/properties/formats/items/properties/format", "type": "string", - "title": "The Format Schema", - "pattern": "^(.*)$" + "title": "label.nameid-formats-format", + "description": "tooltip.nameid-formats-format" }, "value": { - "$id": "#/properties/formats/items/properties/value", "type": "string", - "title": "The Value Schema" + "title": "label.nameid-formats-value", + "description": "tooltip.nameid-formats-value" }, "type": { - "$id": "#/properties/formats/items/properties/type", "type": "string", - "title": "The Type Schema", - "widget": "select", + "title": "label.nameid-formats-type", + "description": "tooltip.nameid-formats-type", + "widget": { + "id": "select" + }, "oneOf": [ { "enum": [ @@ -83,20 +102,6 @@ } } } - }, - "@type": { - "$id": "#/properties/@type", - "type": "string", - "title": "The @type Schema", - "pattern": "^(.*)$" - }, - "version": { - "type": "integer", - "widget": "hidden" - }, - "resourceId": { - "type": "string", - "widget": "hidden" } } } \ No newline at end of file From 2f755df4bf227145aac521b52022c60fbaa96009 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 5 Dec 2018 11:49:23 -0700 Subject: [PATCH 10/38] SHIBUI-799 Added schema to backend --- .../main/resources/nameid-filter.schema.json | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 backend/src/main/resources/nameid-filter.schema.json 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..b52ee8ceb --- /dev/null +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -0,0 +1,107 @@ +{ + "type": "object", + "fieldsets": [ + { + "type": "group", + "fields": [ + "name", + "filterEnabled", + "@type", + "resourceId", + "version", + "removeExistingFormats", + "formats" + ] + } + ], + "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 + }, + "@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": { + "type": "array", + "title": "label.nameid-formats", + "description": "tooltip.nameid-formats", + "items": { + "type": "object", + "properties": { + "format": { + "type": "string", + "title": "label.nameid-formats-format", + "description": "tooltip.nameid-formats-format" + }, + "value": { + "type": "string", + "title": "label.nameid-formats-value", + "description": "tooltip.nameid-formats-value" + }, + "type": { + "type": "string", + "title": "label.nameid-formats-type", + "description": "tooltip.nameid-formats-type", + "widget": { + "id": "select" + }, + "oneOf": [ + { + "enum": [ + "ENTITY" + ], + "description": "value.entity" + }, + { + "enum": [ + "CONDITION_REF" + ], + "description": "value.condition-ref" + }, + { + "enum": [ + "CONDITION_SCRIPT" + ], + "description": "value.condition-script" + } + ] + } + } + } + } + } +} \ No newline at end of file From 8397179363beb9c27b88204d92a0eba71c5aa6a9 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Dec 2018 07:10:00 -0700 Subject: [PATCH 11/38] SHIBUI-799 Implemented UI for NameIDFOrmat Filter --- package-lock.json | 3 -- .../container/new-filter.component.html | 4 +- ui/src/app/schema-form/registry.ts | 4 ++ ui/src/app/schema-form/schema-form.module.ts | 6 ++- .../array/inline-obj-list.component.html | 40 +++++++++++++++++++ .../widget/array/inline-obj-list.component.ts | 11 +++++ .../widget/object/inline-obj.component.html | 9 +++++ .../widget/object/inline-obj.component.ts | 10 +++++ .../assets/schema/filter/nameid.schema.json | 13 +++++- 9 files changed, 92 insertions(+), 8 deletions(-) delete mode 100644 package-lock.json create mode 100644 ui/src/app/schema-form/widget/array/inline-obj-list.component.html create mode 100644 ui/src/app/schema-form/widget/array/inline-obj-list.component.ts create mode 100644 ui/src/app/schema-form/widget/object/inline-obj.component.html create mode 100644 ui/src/app/schema-form/widget/object/inline-obj.component.ts 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/src/app/metadata/filter/container/new-filter.component.html b/ui/src/app/metadata/filter/container/new-filter.component.html index 6168e374d..0e6d05a7d 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.html +++ b/ui/src/app/metadata/filter/container/new-filter.component.html @@ -50,6 +50,4 @@
- -
{{ changes$ | async | json }}
-
{{ schema$ | async | json }}
\ No newline at end of file + \ No newline at end of file diff --git a/ui/src/app/schema-form/registry.ts b/ui/src/app/schema-form/registry.ts index f1b6149a2..266aeb9a2 100644 --- a/ui/src/app/schema-form/registry.ts +++ b/ui/src/app/schema-form/registry.ts @@ -18,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() { @@ -39,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/inline-obj-list.component.html b/ui/src/app/schema-form/widget/array/inline-obj-list.component.html new file mode 100644 index 000000000..af4099b54 --- /dev/null +++ b/ui/src/app/schema-form/widget/array/inline-obj-list.component.html @@ -0,0 +1,40 @@ +
+
+ +    + +   + + + + + + , + {{ error.message }} + + +
+
    +
  • +
    +
    + +
    + +
    +
    +
    +
  • +
+ + {{ 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/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/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index b52ee8ceb..47cb75304 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -9,7 +9,12 @@ "@type", "resourceId", "version", - "removeExistingFormats", + "removeExistingFormats" + ] + }, + { + "type": "group-lg", + "fields": [ "formats" ] } @@ -57,10 +62,16 @@ }, "formats": { "type": "array", + "widget": { + "id": "inline-obj-list" + }, "title": "label.nameid-formats", "description": "tooltip.nameid-formats", "items": { "type": "object", + "widget": { + "id": "inline-obj" + }, "properties": { "format": { "type": "string", From d17f488ee343df1466e8a795d8d45c6d1979f5f3 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Dec 2018 07:16:57 -0700 Subject: [PATCH 12/38] SHIBUI-799 Updated Schema --- .../src/main/resources/nameid-filter.schema.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index b52ee8ceb..47cb75304 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -9,7 +9,12 @@ "@type", "resourceId", "version", - "removeExistingFormats", + "removeExistingFormats" + ] + }, + { + "type": "group-lg", + "fields": [ "formats" ] } @@ -57,10 +62,16 @@ }, "formats": { "type": "array", + "widget": { + "id": "inline-obj-list" + }, "title": "label.nameid-formats", "description": "tooltip.nameid-formats", "items": { "type": "object", + "widget": { + "id": "inline-obj" + }, "properties": { "format": { "type": "string", From d533107addf4ba652da38f739ff3174dc6b38564 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 6 Dec 2018 12:04:29 -0500 Subject: [PATCH 13/38] SHIBUI-799: fix nameid filter init error during effective reload --- .../JPAMetadataResolverServiceImpl.groovy | 3 +- .../opensaml/OpenSamlNameIdFormatFilter.java | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/opensaml/OpenSamlNameIdFormatFilter.java 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 923c88417..64c5c9098 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 @@ -7,6 +7,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteList 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 @@ -99,7 +100,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } if(metadataFilter instanceof NameIdFormatFilter) { NameIdFormatFilter nameIdFormatFilter = NameIdFormatFilter.cast(metadataFilter) - NameIDFormatFilter openSamlTargetFilter = new NameIDFormatFilter() + NameIDFormatFilter openSamlTargetFilter = new OpenSamlNameIdFormatFilter() Map, Collection> predicateRules = [:] nameIdFormatFilter.formats.each { switch (it.type) { 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..1fb1ccc38 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/opensaml/OpenSamlNameIdFormatFilter.java @@ -0,0 +1,34 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters.opensaml; + +import org.opensaml.core.xml.XMLObject; +import org.opensaml.saml.metadata.resolver.filter.FilterException; +import org.opensaml.saml.metadata.resolver.filter.impl.NameIDFormatFilter; +import org.opensaml.saml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; + +import javax.annotation.Nullable; + +/** + * 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 { + + @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; + } +} From fc7cb92755d0ed861d20b468ddf4be7c72918762 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Dec 2018 11:40:49 -0700 Subject: [PATCH 14/38] Updated UI, updated schema --- .../main/resources/nameid-filter.schema.json | 23 ++++++++++++-- .../filter/container/edit-filter.component.ts | 25 +++++++++++----- .../filter/effect/collection.effect.ts | 8 +++++ ui/src/app/metadata/filter/reducer/index.ts | 7 ++++- .../array/inline-obj-list.component.html | 30 ++++++++++++++----- .../assets/schema/filter/nameid.schema.json | 23 ++++++++++++-- 6 files changed, 94 insertions(+), 22 deletions(-) diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index 47cb75304..129f543f2 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -69,14 +69,13 @@ "description": "tooltip.nameid-formats", "items": { "type": "object", + "title": "label.nameid-format", "widget": { "id": "inline-obj" }, "properties": { "format": { - "type": "string", - "title": "label.nameid-formats-format", - "description": "tooltip.nameid-formats-format" + "$ref": "#/definitions/NameIDFormat" }, "value": { "type": "string", @@ -114,5 +113,23 @@ } } } + }, + "definitions": { + "NameIDFormat": { + "type": "string", + "title": "label.nameid-formats-format", + "description": "tooltip.nameid-formats-format", + "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 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 c71e7a489..5ffb8e58e 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 } from 'rxjs/operators'; @Component({ selector: 'edit-filter-page', @@ -25,7 +25,7 @@ export class EditFilterComponent { statusChangeSubject = new Subject<{ value: any[] }>(); private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); - definition: FormDefinition; + definition$: Observable>; schema$: Observable; model$: Observable; @@ -41,9 +41,17 @@ export class EditFilterComponent { private store: Store, private schemaService: SchemaService ) { - this.definition = MetadataFilterTypes.EntityAttributes; - - this.schema$ = this.schemaService.get(this.definition.schema).pipe(shareReplay()); + 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.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); @@ -54,9 +62,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) )) ); @@ -70,6 +79,8 @@ export class EditFilterComponent { this.preview(parameters.id); } }; + + this.definition$.subscribe(d => console.log(d)); } save(): void { diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index 8e640d75e..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,6 +75,13 @@ 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), diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index fdf577027..c1da3cab4 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -79,7 +79,12 @@ 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) => changes ? changes.type : null); +export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => { + const type = changes ? detectFilterType(changes) : null; + console.log(type, changes); + return type; +}); 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 index af4099b54..876ea01f7 100644 --- 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 @@ -21,14 +21,28 @@
  • -
    -
    - -
    - +
    +
    +
    +
    + + +   + + + + (default) + + + +
    +
    +
    + +
    diff --git a/ui/src/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index 47cb75304..129f543f2 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -69,14 +69,13 @@ "description": "tooltip.nameid-formats", "items": { "type": "object", + "title": "label.nameid-format", "widget": { "id": "inline-obj" }, "properties": { "format": { - "type": "string", - "title": "label.nameid-formats-format", - "description": "tooltip.nameid-formats-format" + "$ref": "#/definitions/NameIDFormat" }, "value": { "type": "string", @@ -114,5 +113,23 @@ } } } + }, + "definitions": { + "NameIDFormat": { + "type": "string", + "title": "label.nameid-formats-format", + "description": "tooltip.nameid-formats-format", + "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 From d29c3017022073fc7198003030419228c57e0020 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Dec 2018 13:09:06 -0700 Subject: [PATCH 15/38] SHIBUI-799 Fixed issues with schema --- .../main/resources/nameid-filter.schema.json | 142 ++++++++++-------- .../container/edit-filter.component.html | 54 ++++--- .../filter/container/edit-filter.component.ts | 3 +- ui/src/app/metadata/filter/reducer/index.ts | 1 - .../assets/schema/filter/nameid.schema.json | 142 ++++++++++-------- 5 files changed, 187 insertions(+), 155 deletions(-) diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index 129f543f2..bc8c33cb8 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -2,9 +2,10 @@ "type": "object", "fieldsets": [ { - "type": "group", + "type": "group-lg", "fields": [ "name", + "entityAttributesFilterTarget", "filterEnabled", "@type", "resourceId", @@ -13,7 +14,7 @@ ] }, { - "type": "group-lg", + "type": "group", "fields": [ "formats" ] @@ -35,6 +36,60 @@ "type": "boolean", "default": false }, + "entityAttributesFilterTarget": { + "title": "label.search-criteria", + "description": "tooltip.search-criteria", + "type": "object", + "widget": { + "id": "filter-target" + }, + "properties": { + "entityAttributesFilterTargetType": { + "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", + "entityAttributesFilterTargetType" + ] + }, "@type": { "type": "string", "widget": { @@ -61,75 +116,30 @@ "default": false }, "formats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "definitions": { + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", "type": "array", - "widget": { - "id": "inline-obj-list" - }, - "title": "label.nameid-formats", - "description": "tooltip.nameid-formats", + "uniqueItems": true, "items": { - "type": "object", - "title": "label.nameid-format", + "type": "string", + "minLength": 1, + "maxLength": 255, "widget": { - "id": "inline-obj" - }, - "properties": { - "format": { - "$ref": "#/definitions/NameIDFormat" - }, - "value": { - "type": "string", - "title": "label.nameid-formats-value", - "description": "tooltip.nameid-formats-value" - }, - "type": { - "type": "string", - "title": "label.nameid-formats-type", - "description": "tooltip.nameid-formats-type", - "widget": { - "id": "select" - }, - "oneOf": [ - { - "enum": [ - "ENTITY" - ], - "description": "value.entity" - }, - { - "enum": [ - "CONDITION_REF" - ], - "description": "value.condition-ref" - }, - { - "enum": [ - "CONDITION_SCRIPT" - ], - "description": "value.condition-script" - } - ] - } + "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" + ] } } } - }, - "definitions": { - "NameIDFormat": { - "type": "string", - "title": "label.nameid-formats-format", - "description": "tooltip.nameid-formats-format", - "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 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 e42137ebf..7f2908762 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,45 @@
       - Edit EntityAttributesFilter + Edit Filter {{ type$ | async }}
    -
    - -   - +
    +
    +
    +
    + + +
    +
    +
    + + +   + + +
    +
    +
    -
    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 5ffb8e58e..696de845a 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -32,6 +32,7 @@ export class EditFilterComponent { isSaving$: Observable; filter: MetadataFilter; isValid: boolean; + type$: Observable; validators$: Observable<{ [key: string]: any }>; @@ -80,7 +81,7 @@ export class EditFilterComponent { } }; - this.definition$.subscribe(d => console.log(d)); + this.type$ = this.model$.pipe(map(f => f['@type'])); } save(): void { diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index c1da3cab4..36682ad4d 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -84,7 +84,6 @@ export const detectFilterType = (changes) => changes.type ? changes.type : chang export const getFilterWithChanges = createSelector(getFilter, getSelectedFilter, mergeFn); export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => { const type = changes ? detectFilterType(changes) : null; - console.log(type, changes); return type; }); diff --git a/ui/src/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index 129f543f2..bc8c33cb8 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -2,9 +2,10 @@ "type": "object", "fieldsets": [ { - "type": "group", + "type": "group-lg", "fields": [ "name", + "entityAttributesFilterTarget", "filterEnabled", "@type", "resourceId", @@ -13,7 +14,7 @@ ] }, { - "type": "group-lg", + "type": "group", "fields": [ "formats" ] @@ -35,6 +36,60 @@ "type": "boolean", "default": false }, + "entityAttributesFilterTarget": { + "title": "label.search-criteria", + "description": "tooltip.search-criteria", + "type": "object", + "widget": { + "id": "filter-target" + }, + "properties": { + "entityAttributesFilterTargetType": { + "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", + "entityAttributesFilterTargetType" + ] + }, "@type": { "type": "string", "widget": { @@ -61,75 +116,30 @@ "default": false }, "formats": { + "$ref": "#/definitions/NameIdFormatList" + } + }, + "definitions": { + "NameIdFormatList": { + "title": "label.nameid-format-to-send", + "placeholder": "label.nameid-format", + "description": "tooltip.nameid-format", "type": "array", - "widget": { - "id": "inline-obj-list" - }, - "title": "label.nameid-formats", - "description": "tooltip.nameid-formats", + "uniqueItems": true, "items": { - "type": "object", - "title": "label.nameid-format", + "type": "string", + "minLength": 1, + "maxLength": 255, "widget": { - "id": "inline-obj" - }, - "properties": { - "format": { - "$ref": "#/definitions/NameIDFormat" - }, - "value": { - "type": "string", - "title": "label.nameid-formats-value", - "description": "tooltip.nameid-formats-value" - }, - "type": { - "type": "string", - "title": "label.nameid-formats-type", - "description": "tooltip.nameid-formats-type", - "widget": { - "id": "select" - }, - "oneOf": [ - { - "enum": [ - "ENTITY" - ], - "description": "value.entity" - }, - { - "enum": [ - "CONDITION_REF" - ], - "description": "value.condition-ref" - }, - { - "enum": [ - "CONDITION_SCRIPT" - ], - "description": "value.condition-script" - } - ] - } + "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" + ] } } } - }, - "definitions": { - "NameIDFormat": { - "type": "string", - "title": "label.nameid-formats-format", - "description": "tooltip.nameid-formats-format", - "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 From f78ab5a3b3249017b3cb7b81322f76dca685d7fa Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 08:54:06 -0500 Subject: [PATCH 16/38] SHIBUI-799 - refactor model --- .../JPAMetadataResolverServiceImpl.groovy | 78 ++++++++++++------- .../controller/MetadataFiltersController.java | 1 + .../ui/domain/filters/NameIdFormatFilter.java | 24 ++---- .../filters/NameIdFormatFilterTarget.java | 51 ++++++++++++ .../MetadataFiltersControllerTests.groovy | 1 + ...ymorphicFiltersJacksonHandlingTests.groovy | 43 +++++----- .../repository/FilterRepositoryTests.groovy | 2 +- .../admin/ui/util/TestObjectGenerator.groovy | 26 ++----- backend/src/test/resources/conf/799.xml | 12 --- 9 files changed, 138 insertions(+), 100 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java 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 64c5c9098..cf7c44d8f 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 @@ -37,9 +37,9 @@ import org.w3c.dom.Document import javax.annotation.Nonnull -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.CONDITION_REF -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.CONDITION_SCRIPT -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.ENTITY +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 @@ -62,7 +62,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) { @@ -98,22 +100,25 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { target.setRules(rules) metadataFilters.add(target) } - if(metadataFilter instanceof NameIdFormatFilter) { + if (metadataFilter instanceof NameIdFormatFilter) { NameIdFormatFilter nameIdFormatFilter = NameIdFormatFilter.cast(metadataFilter) NameIDFormatFilter openSamlTargetFilter = new OpenSamlNameIdFormatFilter() Map, Collection> predicateRules = [:] - nameIdFormatFilter.formats.each { - switch (it.type) { - case ENTITY: - predicateRules.put(new EntityIdPredicate([it.value]), [it.format]) - break - case CONDITION_SCRIPT: - predicateRules.put(new ScriptedPredicate(new EvaluableScript(it.value)), [it.format]) - break - default: - // do nothing, we'd have exploded elsewhere previously. - break - } + 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 @@ -171,7 +176,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', @@ -242,7 +247,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 @@ -251,27 +256,39 @@ 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.format) - if(it.type == ENTITY) { - Entity(it.value) - } - else if(it.type == CONDITION_REF) { - ConditionRef(it.value) - } - else if(it.type == CONDITION_SCRIPT) { - def scriptText = it.value + Format(it) + } + switch (type) { + case ENTITY: + filter.nameIdFormatFilterTarget.value.each { + Entity(it) + } + break + case CONDITION_SCRIPT: + case REGEX: ConditionScript() { Script() { - mkp.yieldUnescaped("\n\n") + 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 } } } @@ -493,4 +510,5 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + } \ No newline at end of file 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 0ae56b781..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 @@ -230,6 +230,7 @@ else if (filterWithUpdatedData instanceof NameIdFormatFilter) { 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/filters/NameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java index a2fb316f3..6c67252b0 100644 --- 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 @@ -7,9 +7,11 @@ import lombok.Setter; import lombok.ToString; +import javax.persistence.CascadeType; import javax.persistence.ElementCollection; import javax.persistence.Embeddable; import javax.persistence.Entity; +import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import java.util.List; @@ -28,21 +30,9 @@ public NameIdFormatFilter() { @ElementCollection @OrderColumn - private List formats; - - @Embeddable - @NoArgsConstructor - @AllArgsConstructor - @Getter - @Setter - @EqualsAndHashCode - public static class FormatAndTarget { - private String format; - private String value; - private Type type; - - public enum Type { - ENTITY, CONDITION_REF, CONDITION_SCRIPT - } - } + 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/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 15717ca61..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))) 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 383632119..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 @@ -180,28 +180,27 @@ class PolymorphicFiltersJacksonHandlingTests extends Specification { given: def givenFilterJson = """ { - "@type" : "NameIDFormat", - "name" : "NameIDFormat", - "resourceId" : "20147f53-e368-4911-921a-2e24598e37f8", - "filterEnabled" : true, - "removeExistingFormats" : false, - "formats" : [ { - "format" : "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", - "value" : "https://sp1.example.org", - "type" : "ENTITY" - }, { - "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - "value" : "https://sp2.example.org", - "type" : "ENTITY" - }, { - "format" : "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", - "value" : "conditionRefBeanId", - "type" : "CONDITION_REF" - }, { - "format" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - "value" : "input.getEntityID().equals(\\"https://sp1.example.org\\");", - "type" : "CONDITION_SCRIPT" - } ] + "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: 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 7a733d011..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 @@ -83,6 +83,6 @@ class FilterRepositoryTests extends Specification { then: persistedFilter.audId > 0L - persistedFilter.formats.size() == 4 + persistedFilter.formats.size() == 1 } } 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 482e31007..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,10 +15,7 @@ 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.NameIdFormatFilter.* -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.* -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.* -import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter.FormatAndTarget.Type.* +import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY /** * @author Bill Smith (wsmith@unicon.net) @@ -225,20 +222,13 @@ class TestObjectGenerator { static NameIdFormatFilter nameIdFormatFilter() { return new NameIdFormatFilter().with { it.name = "NameIDFormat" - it.formats = [ - new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - type: Type.ENTITY, value: 'https://sp1.example.org'), - new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress', - type: Type.ENTITY, value: 'https://sp2.example.org'), - new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - type: Type.CONDITION_REF, value: 'conditionRefBeanId'), - new FormatAndTarget( - format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', - type: Type.CONDITION_SCRIPT, value: 'input.getEntityID().equals("https://sp1.example.org");') - ] + 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 } } diff --git a/backend/src/test/resources/conf/799.xml b/backend/src/test/resources/conf/799.xml index b7e32082a..90f43f50d 100644 --- a/backend/src/test/resources/conf/799.xml +++ b/backend/src/test/resources/conf/799.xml @@ -8,17 +8,5 @@ urn:oasis:names:tc:SAML:2.0:nameid-format:persistent https://sp1.example.org - urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress - https://sp2.example.org - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - conditionRefBeanId - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - - - From ebcc1698549cb18b17b5ef07a2514daf74ecfbf7 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 09:56:59 -0500 Subject: [PATCH 17/38] SHIBUI-799 UI JSON Schema REST API wip --- ...dFormatFilterUiDefinitionController.groovy | 59 +++++++++++++++++++ .../JsonSchemaComponentsConfiguration.java | 14 ++++- .../ui/domain/filters/NameIdFormatFilter.java | 3 - .../jsonschema/JsonSchemaLocationLookup.java | 14 +++++ .../JsonSchemaResourceLocation.java | 2 +- .../src/main/resources/application.properties | 1 + 6 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/NameIdFormatFilterUiDefinitionController.groovy 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..8177af7d7 --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/NameIdFormatFilterUiDefinitionController.groovy @@ -0,0 +1,59 @@ +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 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.entityAttributesFiltersSchema +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') +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) { + e.printStackTrace() + 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/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/domain/filters/NameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java index 6c67252b0..608561407 100644 --- 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 @@ -1,15 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; -import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import javax.persistence.CascadeType; import javax.persistence.ElementCollection; -import javax.persistence.Embeddable; import javax.persistence.Entity; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; 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 From 3edaffa95f5224c3b9c109c9357096f856bd6b42 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Dec 2018 08:07:09 -0700 Subject: [PATCH 18/38] Updated schema and filter target component --- .../entity-attributes-filters-ui-schema.json | 3 ++- .../main/resources/nameid-filter.schema.json | 11 ++++++----- .../filter-target.component.html | 10 +++++----- .../filter-target/filter-target.component.ts | 19 +++++++++++++------ .../assets/schema/filter/nameid.schema.json | 11 ++++++----- 5 files changed, 32 insertions(+), 22 deletions(-) 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..86b31393a 100644 --- a/backend/src/main/resources/entity-attributes-filters-ui-schema.json +++ b/backend/src/main/resources/entity-attributes-filters-ui-schema.json @@ -44,7 +44,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/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index bc8c33cb8..cb8a30410 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -5,7 +5,7 @@ "type": "group-lg", "fields": [ "name", - "entityAttributesFilterTarget", + "nameIdFormatFilterTarget", "filterEnabled", "@type", "resourceId", @@ -36,15 +36,16 @@ "type": "boolean", "default": false }, - "entityAttributesFilterTarget": { + "nameIdFormatFilterTarget": { "title": "label.search-criteria", "description": "tooltip.search-criteria", "type": "object", "widget": { - "id": "filter-target" + "id": "filter-target", + "target": "nameIdFormatFilterTargetType" }, "properties": { - "entityAttributesFilterTargetType": { + "nameIdFormatFilterTargetType": { "title": "", "type": "string", "default": "ENTITY", @@ -87,7 +88,7 @@ }, "required": [ "value", - "entityAttributesFilterTargetType" + "nameIdFormatFilterTargetType" ] }, "@type": { 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/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index bc8c33cb8..cb8a30410 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -5,7 +5,7 @@ "type": "group-lg", "fields": [ "name", - "entityAttributesFilterTarget", + "nameIdFormatFilterTarget", "filterEnabled", "@type", "resourceId", @@ -36,15 +36,16 @@ "type": "boolean", "default": false }, - "entityAttributesFilterTarget": { + "nameIdFormatFilterTarget": { "title": "label.search-criteria", "description": "tooltip.search-criteria", "type": "object", "widget": { - "id": "filter-target" + "id": "filter-target", + "target": "nameIdFormatFilterTargetType" }, "properties": { - "entityAttributesFilterTargetType": { + "nameIdFormatFilterTargetType": { "title": "", "type": "string", "default": "ENTITY", @@ -87,7 +88,7 @@ }, "required": [ "value", - "entityAttributesFilterTargetType" + "nameIdFormatFilterTargetType" ] }, "@type": { From a2427e05155f1493d9cbedb435c173b45ffdf3de Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 11:59:15 -0500 Subject: [PATCH 19/38] SHIBUI-799 - integrating with front end --- ...dFormatFilterUiDefinitionController.groovy | 1 - .../JPAMetadataResolverServiceImpl.groovy | 1 + .../opensaml/OpenSamlNameIdFormatFilter.java | 55 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) 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 index 8177af7d7..80bbffe94 100644 --- 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 @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RestController import javax.annotation.PostConstruct -import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.entityAttributesFiltersSchema import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.nameIdFormatFilterSchema import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR 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 cf7c44d8f..c7cf4105a 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 @@ -103,6 +103,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { 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 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 index 1fb1ccc38..7f4fc30ba 100644 --- 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 @@ -1,12 +1,23 @@ 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 @@ -16,6 +27,22 @@ */ 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 { @@ -31,4 +58,32 @@ public XMLObject filter(@Nullable XMLObject metadata) throws FilterException { return metadata; } + + /**Overridden to get rid of log statements which result in NPE in the base class with parent being null**/ + @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); + } + } } From 8e7dc67ec7d59fb91e7cf4851bd5cb8359ceda6f Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 12:51:04 -0500 Subject: [PATCH 20/38] SHIBUI-799 fix test --- ...taSourcesUiDefinitionControllerIntegrationTests.groovy | 8 ++++++++ 1 file changed, 8 insertions(+) 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 From dfe6abf5239c3a1cbf600221637f7a311ecabe6f Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 13:04:03 -0500 Subject: [PATCH 21/38] SHIBUI-799: JSON schema REST API --- backend/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 12bfd55cc..343f0f643 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -109,6 +109,7 @@ custom: - 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 + - DIMA attributeName: http://shibboleth.net/ns/profiles/nameIDFormatPrecedence attributeFriendlyName: nameIDFormatPrecedence - name: authenticationMethods From 901e848ceba0b309faa8a47712b4c9ea05ea7dad Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Fri, 7 Dec 2018 13:05:21 -0500 Subject: [PATCH 22/38] SHIBUI-799: polishing --- backend/src/main/resources/application.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 343f0f643..12bfd55cc 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -109,7 +109,6 @@ custom: - 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 - - DIMA attributeName: http://shibboleth.net/ns/profiles/nameIDFormatPrecedence attributeFriendlyName: nameIDFormatPrecedence - name: authenticationMethods From f5a2f04d8dbe497b41133601c0d6c1979a95cb38 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Dec 2018 13:06:40 -0700 Subject: [PATCH 23/38] SHIBUI-799 Fixed tests --- .../main/resources/nameid-filter.schema.json | 26 +---------- .../container/edit-filter.component.html | 4 +- .../container/edit-filter.component.spec.ts | 14 +++--- .../filter/container/edit-filter.component.ts | 5 +-- .../model/entity-attributes.filter.spec.ts | 4 +- .../filter/model/nameid.filter.spec.ts | 43 +++++++++++++++++++ .../metadata/filter/model/nameid.filter.ts | 31 +++++++++++-- .../filter/reducer/filter.reducer.spec.ts | 34 --------------- .../filter/reducer/search.reducer.spec.ts | 7 +++ 9 files changed, 92 insertions(+), 76 deletions(-) create mode 100644 ui/src/app/metadata/filter/model/nameid.filter.spec.ts diff --git a/backend/src/main/resources/nameid-filter.schema.json b/backend/src/main/resources/nameid-filter.schema.json index cb8a30410..461a9a39b 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -117,30 +117,8 @@ "default": false }, "formats": { - "$ref": "#/definitions/NameIdFormatList" + "$ref": "#/definitions/nameIdFormats" } }, - "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" - ] - } - } - } - } + "definitions": {} } \ No newline at end of file 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 7f2908762..0cf1e5129 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -5,7 +5,9 @@
         - Edit Filter {{ type$ | async }} + + 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..8f7060c27 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 @@ -12,7 +12,7 @@ import { SchemaService } from '../../../schema-form/service/schema.service'; import { HttpClientModule } from '@angular/common/http'; import { MockI18nModule } from '../../../../testing/i18n.stub'; -describe('New Metadata Filter Page', () => { +describe('Edit Metadata Filter Page', () => { let fixture: ComponentFixture; let store: Store; let instance: EditFilterComponent; @@ -25,8 +25,7 @@ describe('New Metadata Filter Page', () => { FormBuilder, NgbPopoverConfig, NavigatorService, - SchemaService, - { provide: WidgetRegistry, useClass: DefaultWidgetRegistry } + SchemaService ], imports: [ StoreModule.forRoot({ @@ -48,7 +47,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 +65,9 @@ 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.preview('foo'); expect(store.dispatch).toHaveBeenCalled(); }); }); @@ -77,21 +76,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 696de845a..a4ee5f3a4 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, filter, switchMap } from 'rxjs/operators'; +import { shareReplay, map, withLatestFrom, filter, switchMap, startWith, defaultIfEmpty } from 'rxjs/operators'; @Component({ selector: 'edit-filter-page', @@ -55,6 +55,7 @@ export class EditFilterComponent { ); 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 => { @@ -80,8 +81,6 @@ export class EditFilterComponent { this.preview(parameters.id); } }; - - this.type$ = this.model$.pipe(map(f => f['@type'])); } save(): void { 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/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 index f98a92eae..d03d66c3e 100644 --- a/ui/src/app/metadata/filter/model/nameid.filter.ts +++ b/ui/src/app/metadata/filter/model/nameid.filter.ts @@ -4,9 +4,34 @@ import { MetadataFilter } from '../../domain/model'; export const NameIDFilter: FormDefinition = { label: 'NameIDFilter', type: 'NameIDFormat', - schema: 'assets/schema/filter/nameid.schema.json', - getValidators(): any { - const validators = {}; + schema: '/api/ui/NameIdFormatFilter', + 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, 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/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', () => { From 8e51c383807075832ee1b250776fbe2de5d4464e Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Dec 2018 14:07:58 -0700 Subject: [PATCH 24/38] Updated text --- .../resources/entity-attributes-filters-ui-schema.json | 3 --- backend/src/main/resources/i18n/messages_en.properties | 5 +++-- backend/src/main/resources/nameid-filter.schema.json | 3 +++ .../metadata/filter/container/new-filter.component.html | 8 ++++++-- .../app/metadata/filter/container/new-filter.component.ts | 2 +- ui/src/assets/schema/filter/nameid.schema.json | 3 +++ 6 files changed, 16 insertions(+), 8 deletions(-) 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 86b31393a..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", diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 32b85ae4c..050c5fc32 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -374,6 +374,8 @@ 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. message.uri-valid-format=URI must be valid format. @@ -429,7 +431,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 @@ -524,7 +526,6 @@ tooltip.velocity-engine=This attribute may be used to specify the name of the Ve 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=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.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 index 461a9a39b..db562abe5 100644 --- a/backend/src/main/resources/nameid-filter.schema.json +++ b/backend/src/main/resources/nameid-filter.schema.json @@ -20,6 +20,9 @@ ] } ], + "required": [ + "name" + ], "properties": { "name": { "title": "label.filter-name", 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 0e6d05a7d..58b46be99 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.html +++ b/ui/src/app/metadata/filter/container/new-filter.component.html @@ -15,9 +15,13 @@
    - +
    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 08f4d509a..7fa2a5c7f 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.ts +++ b/ui/src/app/metadata/filter/container/new-filter.component.ts @@ -41,7 +41,7 @@ export class NewFilterComponent implements OnDestroy, OnInit { validators$: Observable<{ [key: string]: any }>; form: FormGroup = this.fb.group({ - type: [null, Validators.required] + type: ['', Validators.required] }); options$: Observable[]>; diff --git a/ui/src/assets/schema/filter/nameid.schema.json b/ui/src/assets/schema/filter/nameid.schema.json index cb8a30410..2ddb21915 100644 --- a/ui/src/assets/schema/filter/nameid.schema.json +++ b/ui/src/assets/schema/filter/nameid.schema.json @@ -20,6 +20,9 @@ ] } ], + "required": [ + "name" + ], "properties": { "name": { "title": "label.filter-name", From 6cd8cddbce34c238f0228dc41260c62732d82f21 Mon Sep 17 00:00:00 2001 From: Jj! Date: Fri, 7 Dec 2018 15:45:28 -0600 Subject: [PATCH 25/38] [SHIBUI-984] initial work --- .../JPAMetadataResolverServiceImpl.groovy | 45 +++++++++++++++ .../CoreShibUiConfiguration.java | 2 +- .../ui/configuration/ShibUIConfiguration.java | 20 +++++++ ...JPAMetadataResolverServiceImplTests.groovy | 57 +++++++++++++++++++ backend/src/test/resources/conf/984-2.xml | 33 +++++++++++ backend/src/test/resources/conf/984.xml | 33 +++++++++++ .../resources/metadata/984-3-expected.xml | 20 +++++++ backend/src/test/resources/metadata/984-3.xml | 23 ++++++++ 8 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/ShibUIConfiguration.java create mode 100644 backend/src/test/resources/conf/984-2.xml create mode 100644 backend/src/test/resources/conf/984.xml create mode 100644 backend/src/test/resources/metadata/984-3-expected.xml create mode 100644 backend/src/test/resources/metadata/984-3.xml 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 c444bd214..48a74d793 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.service import com.google.common.base.Predicate +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 @@ -52,6 +53,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { @Autowired private MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService + @Autowired + private ShibUIConfiguration shibUIConfiguration + // TODO: enhance @Override void reloadFilters(String metadataResolverResourceId) { @@ -64,6 +68,13 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { List metadataFilters = new ArrayList<>() + // set up namespace protection + if (shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0) { + def target = new org.opensaml.saml.metadata.resolver.filter.impl.EntityAttributesFilter() + target.attributeFilter = new ScriptedPredicate(new EvaluableScript(protectedNamespaceScript())) + metadataFilters.add(target) + } + for (edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter metadataFilter : jpaMetadataResolver.getMetadataFilters()) { if (metadataFilter instanceof EntityAttributesFilter) { EntityAttributesFilter entityAttributesFilter = (EntityAttributesFilter) metadataFilter @@ -104,6 +115,21 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + private String protectedNamespaceScript() { + return """(function (attribute) { + "use strict"; + var namespaces = [${shibUIConfiguration.protectedAttributeNamespaces.collect({"\"${it}\""}).join(', ')}]; + // check the parameter + if (attribute === null) { return true; } + for (var i in namespaces) { + if (attribute.getName().startsWith(namespaces[i])) { + return false; + } + } + return true; + }(input));""" + } + private class ScriptedPredicate extends net.shibboleth.utilities.java.support.logic.ScriptedPredicate { protected ScriptedPredicate(@Nonnull EvaluableScript theScript) { super(theScript) @@ -133,9 +159,18 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) { constructXmlNodeForResolver(mr, delegate) { //TODO: enhance + def didNamespaceProtectionFilter = !(shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0) mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter -> + if (filter instanceof EntityAttributesFilter && !didNamespaceProtectionFilter) { + constructXmlNodeForEntityAttributeNamespaceProtection(delegate) + didNamespaceProtectionFilter = true + } constructXmlNodeForFilter(filter, delegate) } + if (!didNamespaceProtectionFilter) { + constructXmlNodeForEntityAttributeNamespaceProtection(delegate) + didNamespaceProtectionFilter = true + } } } } @@ -144,6 +179,16 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } } + void constructXmlNodeForEntityAttributeNamespaceProtection(def markupBuilderDelegate) { + markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') { + AttributeFilterScript() { + Script() { + mkp.yieldUnescaped("\n\n") + } + } + } + } + void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) { if(filter.xmlShouldBeGenerated()) { markupBuilderDelegate.MetadataFilter(id: filter.name, diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index fc1e0e8b4..fb60112f2 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -47,7 +47,7 @@ import javax.servlet.http.HttpServletRequest; @Configuration -@EnableConfigurationProperties(CustomPropertiesConfiguration.class) +@EnableConfigurationProperties({CustomPropertiesConfiguration.class, ShibUIConfiguration.class}) public class CoreShibUiConfiguration { private static final Logger logger = LoggerFactory.getLogger(CoreShibUiConfiguration.class); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/ShibUIConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/ShibUIConfiguration.java new file mode 100644 index 000000000..953ed2119 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/ShibUIConfiguration.java @@ -0,0 +1,20 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +@ConfigurationProperties(prefix = "shibui") +@Getter +@Setter +public class ShibUIConfiguration { + /** + * A list of namespaces that should be excluded from incoming metadata. This is used to prevent third party metadata + * sources from using attributes that they might not have the rights to use. + */ + private List protectedAttributeNamespaces; +} 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 1e2e61b3c..7184270c3 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 @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.Internationalization import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConverterConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.PlaceholderResolverComponentsConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter @@ -42,7 +43,14 @@ import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input +import spock.lang.Ignore import spock.lang.Specification +import spock.lang.Unroll + +import javax.xml.transform.OutputKeys +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult import static edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers.generatedXmlIsTheSameAsExpectedXml @@ -74,6 +82,9 @@ class JPAMetadataResolverServiceImplTests extends Specification { @Autowired MetadataResolverConverterService mdrConverterService + @Autowired + ShibUIConfiguration shibUIConfiguration + TestObjectGenerator testObjectGenerator DOMBuilder domBuilder @@ -349,6 +360,52 @@ class JPAMetadataResolverServiceImplTests extends Specification { generatedXmlIsTheSameAsExpectedXml('/conf/704.3.xml', domBuilder.parseText(writer.toString())) } + @Unroll + @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) + def 'test namespace protection [#namespaces]'() { + setup: + shibUIConfiguration.protectedAttributeNamespaces = namespaces + def resolver = new DynamicHttpMetadataResolver().with { + it.xmlId = 'DynamicHttpMetadataResolver' + it.metadataRequestURLConstructionScheme = new MetadataQueryProtocolScheme().with { + it.content = 'http://mdq-beta.incommon.org/global' + it + } + it + } + metadataResolverRepository.save(resolver) + + expect: + generatedXmlIsTheSameAsExpectedXml(filename, metadataResolverService.generateConfiguration()) + + where: + namespaces | filename + ['http://shibboleth.net/ns/profiles'] | '/conf/984.xml' + ['http://shibboleth.net/ns/profiles', 'http://scaldingspoon.com/iam'] | '/conf/984-2.xml' + } + + @Ignore('there is a bug in org.opensaml.saml.metadata.resolver.filter.impl.EntityAttributesFilter.applyFilter') + def 'test namespace protection internal filtering'() { + setup: + shibUIConfiguration.protectedAttributeNamespaces = ['http://shibboleth.net/ns/profiles'] + def resolver = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver().with { + it.resourceId = 'testme984' + it.name = 'testme984' + it.classpathMetadataResource = new ClasspathMetadataResource('metadata/984-3.xml') + it + } + def x = metadataResolverRepository.save(resolver) + ((OpenSamlChainingMetadataResolver)metadataResolver).resolvers.add(mdrConverterService.convertToOpenSamlRepresentation(x)) + + when: + metadataResolverService.reloadFilters('testme984') + def ed = metadataResolver.resolveSingle(new CriteriaSet(new EntityIdCriterion('http://test.scaldingspoon.org/test1'))) + def x2 = openSamlObjects.marshalToXmlString(ed) + + then: + !DiffBuilder.compare(Input.fromStream(this.class.getResourceAsStream('/metadata/984-3-expected.xml'))).withTest(Input.fromString(openSamlObjects.marshalToXmlString(ed))).ignoreComments().ignoreWhitespace().build().hasDifferences() + } + static genXmlSnippet(MarkupBuilder xml, Closure xmlNodeGenerator) { xml.MetadataProvider('id': 'ShibbolethMetadata', 'xmlns': 'urn:mace:shibboleth:2.0:metadata', diff --git a/backend/src/test/resources/conf/984-2.xml b/backend/src/test/resources/conf/984-2.xml new file mode 100644 index 000000000..0c5749f10 --- /dev/null +++ b/backend/src/test/resources/conf/984-2.xml @@ -0,0 +1,33 @@ + + + + http://mdq-beta.incommon.org/global + + + + + + + diff --git a/backend/src/test/resources/conf/984.xml b/backend/src/test/resources/conf/984.xml new file mode 100644 index 000000000..5974aa861 --- /dev/null +++ b/backend/src/test/resources/conf/984.xml @@ -0,0 +1,33 @@ + + + + http://mdq-beta.incommon.org/global + + + + + + + diff --git a/backend/src/test/resources/metadata/984-3-expected.xml b/backend/src/test/resources/metadata/984-3-expected.xml new file mode 100644 index 000000000..b15921f7d --- /dev/null +++ b/backend/src/test/resources/metadata/984-3-expected.xml @@ -0,0 +1,20 @@ + + + + + + internal + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + \ No newline at end of file diff --git a/backend/src/test/resources/metadata/984-3.xml b/backend/src/test/resources/metadata/984-3.xml new file mode 100644 index 000000000..87ab7d428 --- /dev/null +++ b/backend/src/test/resources/metadata/984-3.xml @@ -0,0 +1,23 @@ + + + + + + internal + + + givenName + employeeNumber + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + \ No newline at end of file From 81b0fed149f2a2d477b11a1098255e64db47f721 Mon Sep 17 00:00:00 2001 From: Jj! Date: Fri, 7 Dec 2018 15:46:04 -0600 Subject: [PATCH 26/38] [SHIBUI-984] remove debug work --- .../admin/ui/service/JPAMetadataResolverServiceImplTests.groovy | 1 - 1 file changed, 1 deletion(-) 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 7184270c3..94dde95d8 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 @@ -400,7 +400,6 @@ class JPAMetadataResolverServiceImplTests extends Specification { when: metadataResolverService.reloadFilters('testme984') def ed = metadataResolver.resolveSingle(new CriteriaSet(new EntityIdCriterion('http://test.scaldingspoon.org/test1'))) - def x2 = openSamlObjects.marshalToXmlString(ed) then: !DiffBuilder.compare(Input.fromStream(this.class.getResourceAsStream('/metadata/984-3-expected.xml'))).withTest(Input.fromString(openSamlObjects.marshalToXmlString(ed))).ignoreComments().ignoreWhitespace().build().hasDifferences() From 043031285c022d332989e4f70f1e6e07d721d815 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Dec 2018 14:57:42 -0700 Subject: [PATCH 27/38] SHIBUI-799 Removed console log --- ui/src/app/metadata/domain/domain.util.ts | 11 ++++++++--- ui/src/app/metadata/domain/model/metadata-resolver.ts | 1 + .../filter/container/edit-filter.component.spec.ts | 2 ++ .../filter/container/edit-filter.component.ts | 6 +++++- .../metadata/filter/model/entity-attributes.filter.ts | 4 +++- ui/src/app/metadata/filter/model/nameid.filter.ts | 4 ++++ ui/src/app/wizard/model/form-definition.ts | 1 + 7 files changed, 24 insertions(+), 5 deletions(-) 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/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/container/edit-filter.component.spec.ts b/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts index 8f7060c27..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,6 +11,7 @@ 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('Edit Metadata Filter Page', () => { let fixture: ComponentFixture; @@ -67,6 +68,7 @@ describe('Edit Metadata Filter Page', () => { describe('preview method', () => { it('should dispatch a preview action', () => { fixture.detectChanges(); + instance.definition = MetadataFilterTypes.EntityAttributes; instance.preview('foo'); expect(store.dispatch).toHaveBeenCalled(); }); 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 a4ee5f3a4..5143f7dbe 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -26,6 +26,7 @@ export class EditFilterComponent { private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); definition$: Observable>; + definition: FormDefinition; schema$: Observable; model$: Observable; @@ -46,6 +47,9 @@ export class EditFilterComponent { filter(t => !!t), map(t => MetadataFilterTypes[t]) ); + + this.definition$.subscribe(d => this.definition = d); + this.schema$ = this.definition$.pipe( filter(d => !!d), switchMap(d => { @@ -94,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/model/entity-attributes.filter.ts b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts index 83daa063d..78ee24e43 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes.filter.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes.filter.ts @@ -7,6 +7,9 @@ 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) => { @@ -25,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}`, diff --git a/ui/src/app/metadata/filter/model/nameid.filter.ts b/ui/src/app/metadata/filter/model/nameid.filter.ts index d03d66c3e..3787af80b 100644 --- a/ui/src/app/metadata/filter/model/nameid.filter.ts +++ b/ui/src/app/metadata/filter/model/nameid.filter.ts @@ -1,10 +1,14 @@ 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) => { 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 }; From af4dd18a7b5ea4070f432ef04dfa73a0fcafb6ff Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Dec 2018 14:57:57 -0700 Subject: [PATCH 28/38] Added filter construct type for preview --- .../entity/filter/nameid-format-filter.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 ui/src/app/metadata/domain/entity/filter/nameid-format-filter.ts 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' + }; + } +} From 6cbed982edb8b0a76cbfe372287e8bcbd96fccea Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 10 Dec 2018 09:17:29 -0700 Subject: [PATCH 29/38] SHIBUI-1055 Fixed text --- backend/src/main/resources/i18n/messages_en.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 7d20536d0..5c9c78d5a 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -483,9 +483,9 @@ tooltip.enable-provider-upon-saving=If checkbox is clicked, the metadata provide tooltip.max-validity-interval=Defines the window within which the metadata is valid. tooltip.require-signed-root=If true, this fails to load metadata with no signature on the root XML element. tooltip.certificate-file=A key used to verify the signature. Conflicts with trustEngineRef and both of the child elements. -tooltip.retained-roles=Controls whether to keep entity descriptors that contain no roles -tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles. -tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors. +tooltip.retained-roles=Note that property replacement cannot be used on this element. +tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles. Note: If this attribute is set to false, the resulting output may not be schema-valid since an element must include at least one role descriptor. +tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors. Note: If this attribute is set to false, the resulting output may not be schema-valid since an element must include at least one child element, either an element or an element. tooltip.min-refresh-delay=Lower bound on the next refresh from the time calculated based on the metadata\u0027s expiration. tooltip.max-refresh-delay=Upper bound on the next refresh from the time calculated based on the metadata\u0027s expiration. From c48572e5ba66a5c3036a20cf8ca011fe9bcc1644 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 10 Dec 2018 09:38:53 -0700 Subject: [PATCH 30/38] SHIBUI-1050 Fixed min/max refresh delay factor --- .../main/resources/dynamic-http-metadata-provider.schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json index f9dec02b8..4b8dc513f 100644 --- a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json +++ b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json @@ -152,8 +152,8 @@ "step": 0.01 }, "placeholder": "label.real-number", - "minimum": 0, - "maximum": 1, + "minimum": 0.01, + "maximum": 0.99, "default": null }, "minCacheDuration": { From e61050b6f997c9b127cc8f5f26df042baaae89ff Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 10 Dec 2018 11:14:05 -0700 Subject: [PATCH 31/38] SHIBUI-1053 Added element --- ...dynamic-http-metadata-provider.schema.json | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json index 4b8dc513f..887508b7f 100644 --- a/backend/src/main/resources/dynamic-http-metadata-provider.schema.json +++ b/backend/src/main/resources/dynamic-http-metadata-provider.schema.json @@ -222,6 +222,29 @@ "default": null, "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" }, + "removeIdleEntityData": { + "title": "label.remove-idle-entity-data", + "description": "tooltip.remove-idle-entity-data", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, "cleanupTaskInterval": { "title": "label.cleanup-task-interval", "description": "tooltip.cleanup-task-interval", From 3ca883c70a2db8b87dad2fd838a3389fc385c58f Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 10 Dec 2018 19:38:50 -0500 Subject: [PATCH 32/38] SHIBUI-799: code review feedback --- .../NameIdFormatFilterUiDefinitionController.groovy | 5 ++++- settings.gradle | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) 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 index 80bbffe94..8b02efd93 100644 --- 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 @@ -4,6 +4,8 @@ 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 lombok.extern.java.Log import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping @@ -23,6 +25,7 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR */ @RestController @RequestMapping('/api/ui/NameIdFormatFilter') +@Slf4j class NameIdFormatFilterUiDefinitionController { @Autowired @@ -44,7 +47,7 @@ class NameIdFormatFilterUiDefinitionController { return ResponseEntity.ok(parsedJson) } catch (Exception e) { - e.printStackTrace() + log.error(e.getMessage(), e) return ResponseEntity.status(INTERNAL_SERVER_ERROR) .body([jsonParseError : e.getMessage(), sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url]) diff --git a/settings.gradle b/settings.gradle index 7fe93709f..8fae26617 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include 'backend', 'ui', 'pacj-module' \ No newline at end of file +include 'backend', 'ui', 'pac4j-module' \ No newline at end of file From dedbae275ef8944639453e103437bb3da43a0b94 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 10 Dec 2018 19:39:30 -0500 Subject: [PATCH 33/38] SHIBUI-799: polishing --- .../controller/NameIdFormatFilterUiDefinitionController.groovy | 1 - 1 file changed, 1 deletion(-) 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 index 8b02efd93..3dcfcaffe 100644 --- 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 @@ -5,7 +5,6 @@ import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocat import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService import groovy.util.logging.Slf4j -import lombok.extern.java.Log import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping From 166f86b042111303578d9cbbb474303421f8eccd Mon Sep 17 00:00:00 2001 From: Jodie Muramoto Date: Tue, 11 Dec 2018 08:00:06 -0700 Subject: [PATCH 34/38] SHIBUI-1002: Fixed bugs related to filesystemMetadataProvider; --- .../file-system-metadata-provider.schema.json | 4 ++-- backend/src/main/resources/i18n/messages.properties | 2 +- .../src/main/resources/i18n/messages_en.properties | 2 +- .../provider/model/file-system.provider.form.ts | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/main/resources/file-system-metadata-provider.schema.json b/backend/src/main/resources/file-system-metadata-provider.schema.json index 8f28d3d83..95dfbf1f7 100644 --- a/backend/src/main/resources/file-system-metadata-provider.schema.json +++ b/backend/src/main/resources/file-system-metadata-provider.schema.json @@ -8,7 +8,7 @@ ], "properties": { "name": { - "title": "label.metadata-provider-name", + "title": "label.service-provider-name-dashboard-display-only", "description": "tooltip.metadata-provider-name", "type": "string", "widget": { @@ -135,7 +135,7 @@ }, "placeholder": "label.real-number", "minimum": 0, - "maximum": 1, + "maximum": 0.99, "default": null } } diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 25e82fa86..5b39cb7b9 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -299,7 +299,7 @@ label.metadata-provider-type=Metadata Provider Type label.metadata-provider-name=Metadata Provider Name label.select-metadata-type=Select a metadata provider type label.metadata-provider-status=Metadata Provider Status -label.enable-provider-upon-saving=Enable Metadata Provider? +label.enable-provider-upon-saving=If checkbox is clicked, the metadata provider is enabled for integration with the IdP label.certificate-type=Type label.metadata-file=Metadata File diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 7d20536d0..3099c1b8b 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -299,7 +299,7 @@ label.metadata-provider-type=Metadata Provider Type label.metadata-provider-name=Metadata Provider Name label.select-metadata-type=Select a metadata provider type label.metadata-provider-status=Metadata Provider Status -label.enable-provider-upon-saving=Enable Metadata Provider? +label.enable-provider-upon-saving=If checkbox is clicked, the metadata provider is enabled for integration with the IdP label.certificate-type=Type label.metadata-file=Metadata File diff --git a/ui/src/app/metadata/provider/model/file-system.provider.form.ts b/ui/src/app/metadata/provider/model/file-system.provider.form.ts index 9c5e2536b..c42fed449 100644 --- a/ui/src/app/metadata/provider/model/file-system.provider.form.ts +++ b/ui/src/app/metadata/provider/model/file-system.provider.form.ts @@ -44,8 +44,8 @@ export const FileSystemMetadataProviderWizard: Wizard Date: Tue, 11 Dec 2018 11:21:01 -0500 Subject: [PATCH 35/38] Add TODO for parent mapping --- .../tier/shibboleth/admin/ui/domain/AbstractXMLObject.java | 2 ++ 1 file changed, 2 insertions(+) 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 From 24f4d9a0f6220bc5e4dd89f0df1d2cdde1405073 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 11 Dec 2018 11:48:32 -0500 Subject: [PATCH 36/38] Add TODO --- .../domain/filters/opensaml/OpenSamlNameIdFormatFilter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index 7f4fc30ba..90a5f6c18 100644 --- 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 @@ -59,7 +59,9 @@ public XMLObject filter(@Nullable XMLObject metadata) throws FilterException { return metadata; } - /**Overridden to get rid of log statements which result in NPE in the base class with parent being null**/ + /**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) { From afce3a80bd2bcd5ebd02879088af2977f99c77e3 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 12 Dec 2018 10:41:09 -0500 Subject: [PATCH 37/38] Fix location of dynamic http metadata resolver JSON schema --- .../ui/configuration/JsonSchemaComponentsConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e1483d0b4..d70338499 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 @@ -47,7 +47,7 @@ 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:nameid-filter.schema.json"; + private String dynamicHttpMetadataResolverUiSchemaLocation = "classpath:dynamic-http-metadata-provider.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 From 5abca88c25a45a2d535f98d38004473d39fc82d7 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 12 Dec 2018 17:10:26 +0000 Subject: [PATCH 38/38] README.md edited online with Bitbucket --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40788dfe3..a03a53d0b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ For more information, see `docs` ## Requirements -* Java 8 (note that ONLY Java 8 is supported at this time) +* Java 8 (note that ONLY Java 8 is supported at this time; other later versions might work) ## Running