diff --git a/backend/build.gradle b/backend/build.gradle index f1464e63c..fb41a137c 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -5,6 +5,7 @@ plugins { id 'org.springframework.boot' version '2.0.0.RELEASE' id 'com.gorylenko.gradle-git-properties' version '1.4.21' id 'net.researchgate.release' version '2.6.0' + id 'io.franzbecker.gradle-lombok' version '1.13' } apply plugin: 'io.spring.dependency-management' @@ -20,6 +21,12 @@ repositories { } } +configurations.all { + resolutionStrategy { + force 'org.cryptacular:cryptacular:1.1.3' + } +} + processResources.dependsOn(':ui:npm_run_buildProd') //Integration of the frontend and backend into the build to have all of the UI resources available in the app's executable war @@ -41,6 +48,11 @@ springBoot { buildInfo() } +lombok { + version = "1.16.20" + sha256 = "c5178b18caaa1a15e17b99ba5e4023d2de2ebc18b58cde0f5a04ca4b31c10e6d" +} + dependencies { // opensaml deps ['opensaml-saml-api', 'opensaml-saml-impl', 'opensaml-xmlsec-api', 'opensaml-xmlsec-impl'].each { @@ -176,4 +188,4 @@ tasks.withType(JavaExec) { compileJava { dependsOn generateSources -} \ No newline at end of file +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java index 3e9780607..afde9d244 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java @@ -96,6 +96,12 @@ public ResponseEntity update(@RequestBody EntityDescriptorRepresentation edRe if (existingEd == null) { return ResponseEntity.notFound().build(); } + + // Verify we're the only one attempting to update the EntityDescriptor + if (edRepresentation.getVersion() != existingEd.hashCode()) { + return new ResponseEntity(HttpStatus.CONFLICT); + } + EntityDescriptor updatedEd = EntityDescriptor.class.cast(entityDescriptorService.createDescriptorFromRepresentation(edRepresentation)); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java index a818de149..093a5b9ff 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/FilterController.java @@ -10,6 +10,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -98,6 +99,12 @@ public ResponseEntity update(@PathVariable String metadataResolverId, @Reques } EntityAttributesFilter eaf = filters.get(0); + + // Verify we're the only one attempting to update the filter + if (filterRepresentation.getVersion() != eaf.hashCode()) { + return new ResponseEntity(HttpStatus.CONFLICT); + } + // convert our representation so we can get the attributes more easily... EntityAttributesFilter updatedFilter = filterService.createFilterFromRepresentation(filterRepresentation); eaf.setName(updatedFilter.getName()); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java index fe3f06ba8..8f530bdd6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java @@ -8,8 +8,6 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import java.time.LocalDateTime; - import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.GeneratedValue; @@ -17,6 +15,7 @@ import javax.persistence.Id; import javax.persistence.MappedSuperclass; import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; @MappedSuperclass diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AdditionalMetadataLocation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AdditionalMetadataLocation.java index 8aca6caba..c5f33b7b1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AdditionalMetadataLocation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AdditionalMetadataLocation.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AdditionalMetadataLocation extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.AdditionalMetadataLocation { private String locationURI; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliateMember.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliateMember.java index 0039047bf..e5dba9a06 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliateMember.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliateMember.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AffiliateMember extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.AffiliateMember { private String localId; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliationDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliationDescriptor.java index 5122a00f9..d97969c77 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliationDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AffiliationDescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -10,6 +11,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class AffiliationDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.AffiliationDescriptor { private String ownerId; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java index 950e4a6d7..0d82f8a0d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class ArtifactResolutionService extends IndexedEndpoint implements org.opensaml.saml.saml2.metadata.ArtifactResolutionService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java index 905cb7d1b..a89045075 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AssertionConsumerService extends IndexedEndpoint implements org.opensaml.saml.saml2.metadata.AssertionConsumerService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionIDRequestService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionIDRequestService.java index 9c88846b6..9474d1d13 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionIDRequestService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionIDRequestService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AssertionIDRequestService extends Endpoint implements org.opensaml.saml.saml2.metadata.AssertionIDRequestService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java index e3eb808e5..b69b74254 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -12,6 +13,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class Attribute extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.core.Attribute { private String name; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeAuthorityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeAuthorityDescriptor.java index 9b130284d..2dc4d89e4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeAuthorityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeAuthorityDescriptor.java @@ -1,6 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; -import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -11,6 +11,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class AttributeAuthorityDescriptor extends RoleDescriptor implements org.opensaml.saml.saml2.metadata.AttributeAuthorityDescriptor { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java index 7eef7dbd8..dc557400b 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.CascadeType; @@ -11,6 +12,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class AttributeConsumingService extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.AttributeConsumingService { private int acsIndex; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeProfile.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeProfile.java index 9883f8740..21aea9486 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeProfile.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeProfile.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AttributeProfile extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.AttributeProfile { private String profileURI; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeService.java index 07281118d..5855d0294 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AttributeService extends Endpoint implements org.opensaml.saml.saml2.metadata.AttributeService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeValue.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeValue.java index bef88bfa7..11296d558 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeValue.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeValue.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AttributeValue extends AbstractXMLObject implements org.opensaml.saml.saml2.core.AttributeValue { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnAuthorityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnAuthorityDescriptor.java index 33ff25f6a..240705132 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnAuthorityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnAuthorityDescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -10,6 +11,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class AuthnAuthorityDescriptor extends RoleDescriptor implements org.opensaml.saml.saml2.metadata.AuthnAuthorityDescriptor { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnQueryService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnQueryService.java index 2fe2e7b11..275e4eeef 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnQueryService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthnQueryService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AuthnQueryService extends Endpoint implements org.opensaml.saml.saml2.metadata.AuthnQueryService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthzService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthzService.java index 07d7ef713..995071a00 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthzService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AuthzService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class AuthzService extends Endpoint implements org.opensaml.saml.saml2.metadata.AuthzService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Company.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Company.java index 93d93d0d3..54748b91c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Company.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Company.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class Company extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.Company { private String name; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java index 84c7c577b..e266a5555 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration; @@ -10,6 +11,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class ContactPerson extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.ContactPerson { private String contactPersonType; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java index ecd06580f..81b66542d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java @@ -1,10 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Column; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class Description extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.Description { @Column(name = "descriptionXMLLang") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java index c5f394bf5..564b0b0f6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java @@ -1,10 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Column; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class DisplayName extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.DisplayName { @Column(name = "displayNameXMLLan") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java index 59228e45a..313ebd65e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class EmailAddress extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.EmailAddress { private String address; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java index 7fb36e6f1..688dea31d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.xmlsec.encryption.KeySize; import org.opensaml.xmlsec.encryption.OAEPparams; @@ -8,6 +9,7 @@ import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class EncryptionMethod extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.EncryptionMethod { private String algorithm; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java index 94afb1935..97094187a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nonnull; @@ -12,6 +13,7 @@ * Note: This also should extend AbstractElementExtensibleXMLObject, but we can't in Java. */ @Entity +@EqualsAndHashCode(callSuper = true) public class Endpoint extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Endpoint { private String binding; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java index 4740aeb7f..8290dcf40 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.core.Assertion; @@ -13,6 +14,7 @@ import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class EntityAttributes extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.ext.saml2mdattr.EntityAttributes { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilter.java index d673ab4b8..0724a75ea 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilter.java @@ -1,6 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; -import com.google.common.base.Predicate; +import lombok.EqualsAndHashCode; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -8,9 +8,9 @@ import javax.persistence.OneToOne; import java.util.ArrayList; import java.util.List; -import java.util.UUID; @Entity +@EqualsAndHashCode(callSuper = true) public class EntityAttributesFilter extends MetadataFilter { @OneToOne(cascade = CascadeType.ALL) private EntityAttributesFilterTarget entityAttributesFilterTarget; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilterTarget.java index bbd79f811..7b19b072e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilterTarget.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributesFilterTarget.java @@ -1,22 +1,16 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import edu.internet2.tier.shibboleth.admin.ui.controller.FilterController; +import lombok.EqualsAndHashCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.ElementCollection; import javax.persistence.Entity; -import javax.persistence.OneToMany; -import javax.persistence.Transient; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class EntityAttributesFilterTarget extends AbstractAuditable { public enum EntityAttributesFilterTargetType { ENTITY, CONDITION_SCRIPT, CONDITION_REF diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java index c81d1ed43..f8bdd9737 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java @@ -3,6 +3,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.springframework.util.StringUtils; @@ -25,6 +26,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor { private String localId; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java index 3494a3495..dd8521f70 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -9,6 +10,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class Extensions extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Extensions { @Nullable @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java index 85380d1b7..785b60804 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class GivenName extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.GivenName { private String name; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IDPSSODescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IDPSSODescriptor.java index a9efee5ad..2074110e5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IDPSSODescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IDPSSODescriptor.java @@ -1,6 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; -import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.CascadeType; @@ -12,6 +12,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class IDPSSODescriptor extends SSODescriptor implements org.opensaml.saml.saml2.metadata.IDPSSODescriptor { private boolean wantAuthnRequestsSigned; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java index d3ea44b3c..e8d8f1551 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class IndexedEndpoint extends Endpoint implements org.opensaml.saml.saml2.metadata.IndexedEndpoint { private Integer endpointIndex; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java index 3bf42dcaa..45d5477d1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class InformationURL extends AbstractLangBearingURL implements org.opensaml.saml.ext.saml2mdui.InformationURL { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java index fce55b916..0e3a4481e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.security.credential.UsageType; import org.opensaml.xmlsec.signature.KeyInfo; @@ -10,6 +11,7 @@ import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class KeyDescriptor extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.KeyDescriptor { @Column(name = "keyDescriptorName") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java index a996749f4..91fb325ee 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.xmlsec.encryption.AgreementMethod; import org.opensaml.xmlsec.encryption.EncryptedKey; @@ -26,6 +27,7 @@ import java.util.stream.Collectors; @Entity +@EqualsAndHashCode(callSuper = true) public class KeyInfo extends AbstractXMLObject implements org.opensaml.xmlsec.signature.KeyInfo { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyName.java index 90acbeb4d..ae1c913c4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyName.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class KeyName extends XSString implements org.opensaml.xmlsec.signature.KeyName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Keywords.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Keywords.java index ba8b15c5f..7b918ae9a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Keywords.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Keywords.java @@ -1,11 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.ElementCollection; import javax.persistence.Entity; import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class Keywords extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.Keywords { @ElementCollection private List keywords; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java index bc836c5d5..b9173bb22 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java @@ -1,10 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Column; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class Logo extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.Logo { @Column(name = "logUrl") private String url; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java index 0ec74a11e..ec43780d3 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class ManageNameIDService extends Endpoint implements org.opensaml.saml.saml2.metadata.ManageNameIDService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataFilter.java index f5b7c11bd..9bd5e6d2d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataFilter.java @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Column; import javax.persistence.Entity; import java.util.UUID; @@ -8,6 +10,7 @@ * Domain class to store information about {@link org.opensaml.saml.metadata.resolver.filter.MetadataFilter} */ @Entity +@EqualsAndHashCode(callSuper = true) public class MetadataFilter extends AbstractAuditable { private String name; @Column(unique=true) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java index 5ec51e147..9d5288dbc 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/MetadataResolver.java @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.OneToMany; @@ -8,6 +10,7 @@ import java.util.UUID; @Entity +@EqualsAndHashCode(callSuper = true) public class MetadataResolver extends AbstractAuditable { private String name; private String resourceId = UUID.randomUUID().toString(); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java index a9952d4f5..4e63ff301 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class NameIDFormat extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.NameIDFormat { private String format; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDMappingService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDMappingService.java index 02e6de8f7..5bd350538 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDMappingService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDMappingService.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class NameIDMappingService extends Endpoint implements org.opensaml.saml.saml2.metadata.NameIDMappingService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java index 5e65e8b03..1e52e0260 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -9,6 +10,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class Organization extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Organization { @OneToOne(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java index 4e024b9e5..8d473aa82 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class OrganizationDisplayName extends LocalizedName implements org.opensaml.saml.saml2.metadata.OrganizationDisplayName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java index 4d058ebc8..edc9317c1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class OrganizationName extends LocalizedName implements org.opensaml.saml.saml2.metadata.OrganizationName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java index 59aa89800..6af0d7318 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class OrganizationURL extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.OrganizationURL { private String xMLLang; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PDPDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PDPDescriptor.java index 38b3aa161..45053fc71 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PDPDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PDPDescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -10,6 +11,7 @@ @Entity +@EqualsAndHashCode(callSuper = true) public class PDPDescriptor extends RoleDescriptor implements org.opensaml.saml.saml2.metadata.PDPDescriptor { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java index c3f14bfd7..a6d792d80 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class PrivacyStatementURL extends AbstractLangBearingURL implements org.opensaml.saml.ext.saml2mdui.PrivacyStatementURL { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java index 1a841a47e..a7860e931 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java @@ -1,10 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class RequestedAttribute extends Attribute implements org.opensaml.saml.saml2.metadata.RequestedAttribute { private boolean isRequired; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java index a2a111e12..4647c8d47 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.util.AttributeMap; @@ -22,6 +23,7 @@ import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class RoleDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.RoleDescriptor { @ElementCollection diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptorResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptorResolver.java index 5fa37ba63..c2ca42787 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptorResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptorResolver.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import net.shibboleth.utilities.java.support.resolver.CriteriaSet; import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.opensaml.saml.saml2.metadata.RoleDescriptor; @@ -9,6 +10,7 @@ import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class RoleDescriptorResolver extends AbstractAuditable implements org.opensaml.saml.metadata.resolver.RoleDescriptorResolver { private String localId; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java index 9300d9d3f..aeae0164e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.schema.XSBooleanValue; @@ -11,6 +12,7 @@ import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class SPSSODescriptor extends SSODescriptor implements org.opensaml.saml.saml2.metadata.SPSSODescriptor { private Boolean isAuthnRequestsSigned; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java index 04e62fb02..1674b7d99 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import com.google.common.collect.Lists; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -14,6 +15,7 @@ import java.util.List; @Entity +@EqualsAndHashCode(callSuper = true) public class SSODescriptor extends RoleDescriptor implements org.opensaml.saml.saml2.metadata.SSODescriptor { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java index e6365f1f3..4c3a54511 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class ServiceDescription extends LocalizedName implements org.opensaml.saml.saml2.metadata.ServiceDescription { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java index 9e5f3c5ac..b4047f0c8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class ServiceName extends LocalizedName implements org.opensaml.saml.saml2.metadata.ServiceName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java index 6947fa4f2..67967676e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class SingleLogoutService extends Endpoint implements org.opensaml.saml.saml2.metadata.SingleLogoutService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleSignOnService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleSignOnService.java index 41659269b..5d9c59118 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleSignOnService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleSignOnService.java @@ -1,7 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class SingleSignOnService extends Endpoint implements org.opensaml.saml.saml2.metadata.SingleSignOnService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SurName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SurName.java index 535c05071..a62cdeaa6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SurName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SurName.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class SurName extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.SurName { private String name; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/TelephoneNumber.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/TelephoneNumber.java index 03cd97a97..729d5d40f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/TelephoneNumber.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/TelephoneNumber.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class TelephoneNumber extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.TelephoneNumber { private String number; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java index cc694b210..54b1da0b9 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.opensaml.core.xml.XMLObject; @@ -19,6 +20,7 @@ import java.util.stream.Collectors; @Entity +@EqualsAndHashCode(callSuper = true) public class UIInfo extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.UIInfo { @OneToMany @Cascade(CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java index b93451899..2fe5ea571 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java @@ -1,11 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Lob; @Entity +@EqualsAndHashCode(callSuper = true) public class X509Certificate extends AbstractXMLObject implements org.opensaml.xmlsec.signature.X509Certificate { @Column(name = "x509CertificateValue") @Lob diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java index e2f257ccb..dabb50332 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.XMLObject; import org.opensaml.xmlsec.signature.X509CRL; import org.opensaml.xmlsec.signature.X509Certificate; @@ -20,6 +21,7 @@ import java.util.stream.Collectors; @Entity +@EqualsAndHashCode(callSuper = true) public class X509Data extends AbstractXMLObject implements org.opensaml.xmlsec.signature.X509Data { @OneToMany(cascade = CascadeType.ALL) List xmlObjects = new ArrayList<>(); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java index 9dd447277..53b8357f5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.util.AttributeMap; import javax.annotation.Nonnull; @@ -8,6 +9,7 @@ import javax.persistence.Transient; @Entity +@EqualsAndHashCode(callSuper = true) public class XSAny extends AbstractElementExtensibleXMLObject implements org.opensaml.core.xml.schema.XSAny { private String textContext; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java index a0df05fde..0d886d52a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class XSBase64Binary extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSBase64Binary { private String b64value; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java index 369f10719..72399bf06 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.annotation.Nullable; @@ -7,6 +8,7 @@ import javax.persistence.Transient; @Entity +@EqualsAndHashCode(callSuper = true) public class XSBoolean extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSBoolean { private String storedValue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java index c88f75917..0ea3116f8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import org.joda.time.DateTime; import org.joda.time.chrono.ISOChronology; import org.joda.time.format.DateTimeFormatter; @@ -11,6 +12,7 @@ import javax.persistence.Transient; @Entity +@EqualsAndHashCode(callSuper = true) public class XSDateTime extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSDateTime { private DateTime dateTime; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java index 82ffc6329..294d0a575 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class XSInteger extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSInteger { private int intValue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java index e17a93863..259e3df36 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; import net.shibboleth.utilities.java.support.xml.QNameSupport; import javax.annotation.Nullable; @@ -8,6 +9,7 @@ import java.beans.Transient; @Entity +@EqualsAndHashCode(callSuper = true) public class XSQName extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSQName { @Nullable @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java index 5ad9264f6..f35e8e287 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class XSString extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSString { private String xsStringvalue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java index 8e2a6c02c..2780c5472 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java @@ -1,10 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import lombok.EqualsAndHashCode; + import javax.annotation.Nullable; import javax.persistence.Column; import javax.persistence.Entity; @Entity +@EqualsAndHashCode(callSuper = true) public class XSURI extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSURI { @Column(name = "xsuriValue") private String value; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java index 3bab8e4b0..c684d458a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java @@ -9,6 +9,8 @@ public class EntityDescriptorRepresentation implements Serializable { + private int version; + public EntityDescriptorRepresentation() { } @@ -193,4 +195,12 @@ public List getAttributeRelease() { public void setAttributeRelease(List attributeRelease) { this.attributeRelease = attributeRelease; } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java index ce0bc26f8..b9e2f1213 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java @@ -13,6 +13,7 @@ public class FilterRepresentation implements Serializable { private List attributeRelease; private LocalDateTime createdDate; private LocalDateTime modifiedDate; + private int version; public FilterRepresentation() { @@ -87,4 +88,12 @@ public LocalDateTime getModifiedDate() { public void setModifiedDate(LocalDateTime modifiedDate) { this.modifiedDate = modifiedDate; } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterTargetRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterTargetRepresentation.java index 773178cf0..dbb4b5c60 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterTargetRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterTargetRepresentation.java @@ -10,6 +10,7 @@ public class FilterTargetRepresentation { private String type; private List value; + private int version; public FilterTargetRepresentation() { @@ -48,4 +49,8 @@ public List getValue() { public void setValue(List listValue) { this.value = listValue; } + + public void setVersion(int version) { + this.version = version; + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java index fbb85f6d2..5a4a65a25 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java @@ -11,6 +11,7 @@ */ public interface EntityService { List getAttributeListFromEntityRepresentation(EntityDescriptorRepresentation entityDescriptorRepresentation); + edu.internet2.tier.shibboleth.admin.ui.domain.Attribute getAttributeFromAttributeReleaseList(List attributeReleaseList); List getAttributeListFromAttributeReleaseList(List attributeReleaseList); List getAttributeListFromRelyingPartyOverridesRepresentation(RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index 922f94df7..9f3da24d3 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -71,9 +71,6 @@ public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService { @Autowired private EntityService entityService; - public JPAEntityDescriptorServiceImpl() { - } - public JPAEntityDescriptorServiceImpl(OpenSamlObjects openSamlObjects, EntityService entityService) { this.openSamlObjects = openSamlObjects; this.entityService = entityService; @@ -350,6 +347,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope representation.setServiceEnabled(ed.isServiceEnabled()); representation.setCreatedDate(ed.getCreatedDate()); representation.setModifiedDate(ed.getModifiedDate()); + representation.setVersion(ed.hashCode()); if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getSupportedProtocols().size() > 0) { ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptorRepresentation = representation.getServiceProviderSsoDescriptor(true); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java index ff5c3981a..263f0c01e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java @@ -22,8 +22,6 @@ public class JPAEntityServiceImpl implements EntityService { @Autowired private AttributeUtility attributeUtility; - public JPAEntityServiceImpl() {} - public JPAEntityServiceImpl(OpenSamlObjects openSamlObjects) { this.openSamlObjects = openSamlObjects; } @@ -32,55 +30,40 @@ public JPAEntityServiceImpl(OpenSamlObjects openSamlObjects) { public List getAttributeListFromEntityRepresentation(EntityDescriptorRepresentation entityDescriptorRepresentation) { List list = new ArrayList<>(); if (entityDescriptorRepresentation.getRelyingPartyOverrides() != null) { - // Let's do the overrides - RelyingPartyOverridesRepresentation overridesRepresentation = entityDescriptorRepresentation.getRelyingPartyOverrides(); - if (overridesRepresentation.isSignAssertion()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.SIGN_ASSERTIONS, MDDCConstants.SIGN_ASSERTIONS_FN, true)); - } - if (overridesRepresentation.isDontSignResponse()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.SIGN_RESPONSES, MDDCConstants.SIGN_RESPONSES_FN, false)); - } - if (overridesRepresentation.isTurnOffEncryption()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.ENCRYPT_ASSERTIONS, MDDCConstants.ENCRYPT_ASSERTIONS_FN, false)); - } - if (overridesRepresentation.isUseSha()) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.SECURITY_CONFIGURATION, MDDCConstants.SECURITY_CONFIGURATION_FN, "shibboleth.SecurityConfiguration.SHA1")); - } - if (overridesRepresentation.isIgnoreAuthenticationMethod()) { - // this is actually going to be wrong, but it will work for the time being. this should be a bitmask value that we calculate - // TODO: fix - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.DISALLOWED_FEATURES, MDDCConstants.DISALLOWED_FEATURES_FN, "0x1")); - } - if (overridesRepresentation.isOmitNotBefore()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE, MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE_FN, false)); - } - if (overridesRepresentation.getResponderId() != null && !"".equals(overridesRepresentation.getResponderId())) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.RESPONDER_ID, MDDCConstants.RESPONDER_ID_FN, overridesRepresentation.getResponderId())); - } - if (overridesRepresentation.getNameIdFormats() != null && overridesRepresentation.getNameIdFormats().size() > 0) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.NAME_ID_FORMAT_PRECEDENCE, MDDCConstants.NAME_ID_FORMAT_PRECEDENCE_FN, overridesRepresentation.getNameIdFormats())); - } - if (overridesRepresentation.getAuthenticationMethods() != null && overridesRepresentation.getAuthenticationMethods().size() > 0) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.DEFAULT_AUTHENTICATION_METHODS, MDDCConstants.DEFAULT_AUTHENTICATION_METHODS_FN, overridesRepresentation.getAuthenticationMethods())); - } + getAttributeListFromRelyingPartyOverridesRepresentation(entityDescriptorRepresentation.getRelyingPartyOverrides()).forEach(attribute -> + list.add((edu.internet2.tier.shibboleth.admin.ui.domain.Attribute) attribute) + ); } // let's map the attribute release if (entityDescriptorRepresentation.getAttributeRelease() != null && entityDescriptorRepresentation.getAttributeRelease().size() > 0) { - edu.internet2.tier.shibboleth.admin.ui.domain.Attribute attribute = ((AttributeBuilder) openSamlObjects.getBuilderFactory().getBuilder(edu.internet2.tier.shibboleth.admin.ui.domain.Attribute.DEFAULT_ELEMENT_NAME)).buildObject(); - list.add(attribute); - - attribute.setName(MDDCConstants.RELEASE_ATTRIBUTES); - - for (String attributeRelease : entityDescriptorRepresentation.getAttributeRelease()) { - XSString xsString = (XSString) openSamlObjects.getBuilderFactory().getBuilder(XSString.TYPE_NAME).buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); - xsString.setValue(attributeRelease); - attribute.getAttributeValues().add(xsString); - } + list.add(getAttributeFromAttributeReleaseList(entityDescriptorRepresentation.getAttributeRelease())); } + return (List)(List)list; } + @Override + public edu.internet2.tier.shibboleth.admin.ui.domain.Attribute getAttributeFromAttributeReleaseList(List attributeReleaseList) { + edu.internet2.tier.shibboleth.admin.ui.domain.Attribute attribute = ((AttributeBuilder) openSamlObjects + .getBuilderFactory() + .getBuilder(edu.internet2.tier.shibboleth.admin.ui.domain.Attribute.DEFAULT_ELEMENT_NAME)) + .buildObject(); + + attribute.setName(MDDCConstants.RELEASE_ATTRIBUTES); + + attributeReleaseList.forEach(attributeRelease -> { + XSString xsString = (XSString) openSamlObjects + .getBuilderFactory() + .getBuilder(XSString.TYPE_NAME) + .buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + xsString.setValue(attributeRelease); + attribute.getAttributeValues().add(xsString); + }); + + return attribute; + } + @Override public List getAttributeListFromAttributeReleaseList(List attributeReleaseList) { List attributeList = new ArrayList<>(); @@ -130,7 +113,4 @@ public List getAttributeListFromRelyingPartyOverridesRepresentation(R return (List)(List)list; } - - - } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java index a7bc130e4..8b431f62f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java @@ -62,6 +62,8 @@ public FilterRepresentation createRepresentationFromFilter(EntityAttributesFilte entityDescriptorService.getRelyingPartyOverridesRepresentationFromAttributeList(entityAttributesFilter.getAttributes())); representation.setFilterTarget(filterTargetService.createRepresentationFromFilterTarget(entityAttributesFilter.getEntityAttributesFilterTarget())); + + representation.setVersion(entityAttributesFilter.hashCode()); return representation; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImpl.java index a28d5a5ee..2e0739d76 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImpl.java @@ -24,6 +24,7 @@ public FilterTargetRepresentation createRepresentationFromFilterTarget(EntityAtt representation.setValue(entityAttributesFilterTarget.getValue()); representation.setType(entityAttributesFilterTarget.getEntityAttributesFilterTargetType().name()); + representation.setVersion(entityAttributesFilterTarget.hashCode()); return representation; } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DefaultAuthenticationIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DefaultAuthenticationIntegrationTests.groovy new file mode 100644 index 000000000..43b6c850e --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/DefaultAuthenticationIntegrationTests.groovy @@ -0,0 +1,36 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.util.DefaultUriBuilderFactory +import spock.lang.Specification + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("default") +class DefaultAuthenticationIntegrationTests extends Specification { + + @Autowired + private WebTestClient webClient + + def setup() { + this.webClient.webClient.uriBuilderFactory.encodingMode = DefaultUriBuilderFactory.EncodingMode.NONE + } + + def "When auth is enabled and an unauth'd request is made, a 302 is returned which points at login"() { + when: + def result = this.webClient + .get() + .uri("/api/entities/http%3A%2F%2Ftest.scaldingspoon.org%2Ftest1") + .exchange() + + then: + result + .expectStatus().isEqualTo(302) + .expectHeader().valueMatches("Location", "http://localhost:\\d*/login") + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy index 205e91e9b..739243d24 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy @@ -1,41 +1,62 @@ package edu.internet2.tier.shibboleth.admin.ui.controller +import com.fasterxml.jackson.databind.ObjectMapper import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import groovy.json.JsonOutput +import groovy.json.JsonSlurper import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.web.client.RestTemplate import spock.lang.Specification import spock.lang.Subject import java.time.LocalDateTime import static org.hamcrest.CoreMatchers.containsString -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import static org.springframework.http.MediaType.* +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.* class EntityDescriptorControllerTests extends Specification { + RandomGenerator randomGenerator + TestObjectGenerator generator + + def mapper + def service def entityDescriptorRepository = Mock(EntityDescriptorRepository) + def mockRestTemplate = Mock(RestTemplate) def openSamlObjects = new OpenSamlObjects().with { init() it } - @Subject - def controller = new EntityDescriptorController ( - entityDescriptorRepository: entityDescriptorRepository, - openSamlObjects: openSamlObjects, - entityDescriptorService: new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects)) - ) - + def mockMvc - def mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + @Subject + def controller + + def setup() { + generator = new TestObjectGenerator() + randomGenerator = new RandomGenerator() + mapper = new ObjectMapper() + service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects)) + + controller = new EntityDescriptorController( + entityDescriptorRepository: entityDescriptorRepository, + openSamlObjects: openSamlObjects, + entityDescriptorService: service + ) + controller.restTemplate = mockRestTemplate + mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + } def 'GET /EntityDescriptors with empty repository'() { given: @@ -59,9 +80,10 @@ class EntityDescriptorControllerTests extends Specification { def 'GET /EntityDescriptors with 1 record in repository'() { given: def expectedCreationDate = '2017-10-23T11:11:11' - def oneRecordFromRepository = - [new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate))].stream() + def entityDescriptor = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate)) + def oneRecordFromRepository = [entityDescriptor].stream() + def version = entityDescriptor.hashCode() def expectedOneRecordListResponseBody = """ [ { @@ -79,7 +101,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "version": $version } ] """ @@ -102,12 +125,15 @@ class EntityDescriptorControllerTests extends Specification { def 'GET /EntityDescriptors with 2 records in repository'() { given: def expectedCreationDate = '2017-10-23T11:11:11' - def twoRecordsFromRepository = [new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)), - new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', - serviceEnabled: false, - createdDate: LocalDateTime.parse(expectedCreationDate))].stream() + createdDate: LocalDateTime.parse(expectedCreationDate)) + def versionOne = entityDescriptorOne.hashCode() + def entityDescriptorTwo = new EntityDescriptor(resourceId: 'uuid-2', entityID: 'eid2', serviceProviderName: 'sp2', + serviceEnabled: false, + createdDate: LocalDateTime.parse(expectedCreationDate)) + def versionTwo = entityDescriptorTwo.hashCode() + def twoRecordsFromRepository = [entityDescriptorOne, entityDescriptorTwo].stream() def expectedTwoRecordsListResponseBody = """ [ { @@ -125,7 +151,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "version": $versionOne }, { "id": "uuid-2", @@ -142,7 +169,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "version": $versionTwo } ] """ @@ -170,6 +198,10 @@ class EntityDescriptorControllerTests extends Specification { def expectedUUID = 'uuid-1' def expectedResponseHeader = 'Location' def expectedResponseHeaderValue = "/api/EntityDescriptor/$expectedUUID" + def entityDescriptor = new EntityDescriptor(resourceId: expectedUUID, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate)) + def version = entityDescriptor.hashCode() def postedJsonBody = """ { @@ -208,7 +240,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "version": $version } """ @@ -227,9 +260,7 @@ class EntityDescriptorControllerTests extends Specification { it.entityID == expectedEntityId && it.serviceProviderName == expectedSpName && it.serviceEnabled == true - }) >> new EntityDescriptor(resourceId: expectedUUID, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) + }) >> entityDescriptor result.andExpect(status().isCreated()) .andExpect(content().json(expectedJsonBody, true)) @@ -292,6 +323,11 @@ class EntityDescriptorControllerTests extends Specification { def expectedSpName = 'sp1' def expectedEntityId = 'eid1' + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate)) + def version = entityDescriptor.hashCode() + def expectedJsonBody = """ { "id": "${providedResourceId}", @@ -309,7 +345,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "version": $version } """ @@ -318,12 +355,303 @@ class EntityDescriptorControllerTests extends Specification { then: //EntityDescriptor found - 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> - new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, - serviceEnabled: true, - createdDate: LocalDateTime.parse(expectedCreationDate)) + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + result.andExpect(status().isOk()) .andExpect(content().json(expectedJsonBody, true)) } + + def 'GET /EntityDescriptor/{resourceId} existing (xml)'() { + given: + def expectedCreationDate = '2017-10-23T11:11:11' + def providedResourceId = 'uuid-1' + def expectedSpName = 'sp1' + def expectedEntityId = 'eid1' + + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate)) + entityDescriptor.setElementLocalName("EntityDescriptor") + entityDescriptor.setNamespacePrefix("md") + entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + + def expectedXML = """ +""" + + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId") + .accept(APPLICATION_XML)) + + then: + //EntityDescriptor found + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + + + result.andExpect(status().isOk()) + .andExpect(content().xml(expectedXML)) + } + + def "POST /EntityDescriptor handles XML happily"() { + given: + def postedBody = ''' + + + + + internal + + + givenName + employeeNumber + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + +''' + def spName = randomGenerator.randomString() + + def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(postedBody.bytes)) + + 1 * entityDescriptorRepository.findByEntityID(_) >> null + 1 * entityDescriptorRepository.save(_) >> expectedEntityDescriptor + + def expectedJson = """ +{ + "version": ${expectedEntityDescriptor.hashCode()}, + "id": "${expectedEntityDescriptor.resourceId}", + "serviceProviderName": null, + "entityId": "http://test.scaldingspoon.org/test1", + "organization": null, + "contacts": null, + "mdui": null, + "serviceProviderSsoDescriptor": { + "protocolSupportEnum": "SAML 2", + "nameIdFormats": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + ] + }, + "logoutEndpoints": null, + "securityInfo": null, + "assertionConsumerServices": [ + { + "locationUrl": "https://test.scaldingspoon.org/test1/acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + "makeDefault": false + } + ], + "serviceEnabled": false, + "createdDate": null, + "modifiedDate": null, + "relyingPartyOverrides": { + "signAssertion": false, + "dontSignResponse": false, + "turnOffEncryption": false, + "useSha": false, + "ignoreAuthenticationMethod": false, + "omitNotBefore": false, + "responderId": null, + "nameIdFormats": [], + "authenticationMethods": [] + }, + "attributeRelease": [ + "givenName", + "employeeNumber" + ] +} +""" + + when: + def result = mockMvc.perform(post("/api/EntityDescriptor") + .contentType(APPLICATION_XML) + .content(postedBody) + .param("spName", spName)) + + + then: + result.andExpect(status().isCreated()) + .andExpect(content().json(expectedJson, true)) + } + + def "POST /EntityDescriptor returns error for duplicate entity id"() { + given: + def postedBody = ''' + + + + + internal + + + givenName + employeeNumber + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + +''' + def spName = randomGenerator.randomString() + + def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(postedBody.bytes)) + + 1 * entityDescriptorRepository.findByEntityID(expectedEntityDescriptor.entityID) >> expectedEntityDescriptor + 0 * entityDescriptorRepository.save(_) + + when: + def result = mockMvc.perform(post("/api/EntityDescriptor") + .contentType(APPLICATION_XML) + .content(postedBody) + .param("spName", spName)) + + + then: + result.andExpect(status().isConflict()) + .andExpect(content().string("The entity descriptor with entity id [http://test.scaldingspoon.org/test1] already exists.")) + } + + def "POST /EntityDescriptor handles x-www-form-urlencoded happily"() { + given: + def postedMetadataUrl = "http://test.scaldingspoon.org/test1" + def restXml = ''' + + + + + internal + + + givenName + employeeNumber + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + +''' + + def spName = randomGenerator.randomString() + + def expectedEntityDescriptor = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(restXml.bytes)) + + 1 * mockRestTemplate.getForObject(_, _) >> restXml.bytes + 1 * entityDescriptorRepository.findByEntityID(_) >> null + 1 * entityDescriptorRepository.save(_) >> expectedEntityDescriptor + + def expectedJson = """ +{ + "version": ${expectedEntityDescriptor.hashCode()}, + "id": "${expectedEntityDescriptor.resourceId}", + "serviceProviderName": null, + "entityId": "http://test.scaldingspoon.org/test1", + "organization": null, + "contacts": null, + "mdui": null, + "serviceProviderSsoDescriptor": { + "protocolSupportEnum": "SAML 2", + "nameIdFormats": [ + "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + ] + }, + "logoutEndpoints": null, + "securityInfo": null, + "assertionConsumerServices": [ + { + "locationUrl": "https://test.scaldingspoon.org/test1/acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + "makeDefault": false + } + ], + "serviceEnabled": false, + "createdDate": null, + "modifiedDate": null, + "relyingPartyOverrides": { + "signAssertion": false, + "dontSignResponse": false, + "turnOffEncryption": false, + "useSha": false, + "ignoreAuthenticationMethod": false, + "omitNotBefore": false, + "responderId": null, + "nameIdFormats": [], + "authenticationMethods": [] + }, + "attributeRelease": [ + "givenName", + "employeeNumber" + ] +} +""" + + when: + def result = mockMvc.perform(post("/api/EntityDescriptor") + .contentType(APPLICATION_FORM_URLENCODED) + .param("metadataUrl", postedMetadataUrl) + .param("spName", spName)) + + + then: + result.andExpect(status().isCreated()) + .andExpect(content().json(expectedJson, true)) + } + + def "PUT /EntityDescriptor updates entity descriptors properly"() { + given: + def entityDescriptor = generator.buildEntityDescriptor() + def updatedEntityDescriptor = generator.buildEntityDescriptor() + updatedEntityDescriptor.resourceId = entityDescriptor.resourceId + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) + updatedEntityDescriptorRepresentation.version = entityDescriptor.hashCode() + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) + + def resourceId = entityDescriptor.resourceId + + 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor + 1 * entityDescriptorRepository.save(_) >> updatedEntityDescriptor + + when: + def result = mockMvc.perform( + put("/api/EntityDescriptor/$resourceId") + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + def expectedJson = new JsonSlurper().parseText(postedJsonBody) + expectedJson << [version: updatedEntityDescriptor.hashCode()] + result.andExpect(status().isOk()) + .andExpect(content().json(JsonOutput.toJson(expectedJson), true)) + } + + def "PUT /EntityDescriptor 409's if the version numbers don't match"() { + given: + def entityDescriptor = generator.buildEntityDescriptor() + def updatedEntityDescriptor = generator.buildEntityDescriptor() + updatedEntityDescriptor.resourceId = entityDescriptor.resourceId + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) + + def resourceId = entityDescriptor.resourceId + + 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor + + when: + def result = mockMvc.perform( + put("/api/EntityDescriptor/$resourceId") + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().is(409)) + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy index 0c899d4f7..fdf90d65d 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/FilterControllerTests.groovy @@ -12,6 +12,8 @@ import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import groovy.json.JsonOutput +import groovy.json.JsonSlurper import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -166,8 +168,9 @@ class FilterControllerTests extends Specification { def randomFilter = testObjectGenerator.buildEntityAttributesFilter() def updatedFilter = testObjectGenerator.buildEntityAttributesFilter() updatedFilter.resourceId = randomFilter.resourceId - def postedJsonBody = mapper.writeValueAsString( - filterService.createRepresentationFromFilter(updatedFilter)) + def updatedFilterRepresentation = filterService.createRepresentationFromFilter(updatedFilter) + updatedFilterRepresentation.setVersion(randomFilter.hashCode()) + def postedJsonBody = mapper.writeValueAsString(updatedFilterRepresentation) def originalMetadataResolver = new MetadataResolver() originalMetadataResolver.setResourceId(randomGenerator.randomId()) @@ -190,8 +193,37 @@ class FilterControllerTests extends Specification { .content(postedJsonBody)) then: + def expectedJson = new JsonSlurper().parseText(postedJsonBody) + expectedJson << [version: updatedFilter.hashCode()] result.andExpect(status().isOk()) - .andExpect(content().json(postedJsonBody, true)) + .andExpect(content().json(JsonOutput.toJson(expectedJson), true)) + } + + def "FilterController.update 409's if the version numbers don't match"() { + given: + def randomFilter = testObjectGenerator.buildEntityAttributesFilter() + def updatedFilter = testObjectGenerator.buildEntityAttributesFilter() + updatedFilter.resourceId = randomFilter.resourceId + def postedJsonBody = mapper.writeValueAsString( + filterService.createRepresentationFromFilter(updatedFilter)) + + def originalMetadataResolver = new MetadataResolver() + originalMetadataResolver.setResourceId(randomGenerator.randomId()) + originalMetadataResolver.setMetadataFilters(testObjectGenerator.buildFilterList()) + originalMetadataResolver.getMetadataFilters().add(randomFilter) + + 1 * metadataResolverRepository.findAll() >> [originalMetadataResolver] + + def filterUUID = randomFilter.getResourceId() + + when: + def result = mockMvc.perform( + put("/api/MetadataResolver/foo/Filter/$filterUUID") + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().is(409)) } EntityAttributesFilter chooseRandomFilterFromList(List filters) { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy new file mode 100644 index 000000000..a3b8fdf7c --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy @@ -0,0 +1,143 @@ +package edu.internet2.tier.shibboleth.admin.ui.scheduled + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.xmlunit.builder.DiffBuilder +import org.xmlunit.builder.Input +import spock.lang.Specification + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class EntityDescriptorFilesScheduledTasksTests extends Specification { + + def tempPath = "/tmp/shibui" + + def directory + + @Autowired + OpenSamlObjects openSamlObjects + + def entityDescriptorRepository = Mock(EntityDescriptorRepository) + + def entityDescriptorFilesScheduledTasks + + def service + + def randomGenerator + + def setup() { + randomGenerator = new RandomGenerator() + tempPath = tempPath + randomGenerator.randomRangeInt(10000, 20000) + service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects)) + entityDescriptorFilesScheduledTasks = new EntityDescriptorFilesScheduledTasks(tempPath, entityDescriptorRepository, openSamlObjects) + directory = new File(tempPath) + directory.mkdir() + } + + def "generateEntityDescriptorFiles properly generates a file from an Entity Descriptor"() { + given: + def expectedXml = ''' + + + name + display name + http://test.example.org + + + ''' + + def entityDescriptor = service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { + it.entityId = 'http://test.example.org/test1' + it.organization = new OrganizationRepresentation().with { + it.name = 'name' + it.displayName = 'display name' + it.url = 'http://test.example.org' + it + } + it + }) + 1 * entityDescriptorRepository.findAllByServiceEnabled(true) >> [entityDescriptor].stream() + + when: + if (directory.exists()) { + entityDescriptorFilesScheduledTasks.generateEntityDescriptorFiles() + } else { + throw new RuntimeException("temp directory does not exist!") + } + + then: + def files = new File(tempPath).listFiles({d, f -> f ==~ /.*.xml/ } as FilenameFilter) + files.size() == 1 + def result = files[0].text + def diff = DiffBuilder.compare(Input.fromString(expectedXml)).withTest(Input.fromString(result)).ignoreComments().ignoreWhitespace().build() + !diff.hasDifferences() + } + + def "removeDanglingEntityDescriptorFiles properly deletes files"() { + given: + def expectedXml = ''' + + + name + display name + http://test.example.org + + + ''' + + def entityDescriptor = service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { + it.entityId = 'http://test.example.org/test1' + it.organization = new OrganizationRepresentation().with { + it.name = 'name' + it.displayName = 'display name' + it.url = 'http://test.example.org' + it + } + it + }) + + def file = new File(directory, randomGenerator.randomId() + ".xml") + file.text = "Delete me!" + + 1 * entityDescriptorRepository.findAllByServiceEnabled(true) >> [entityDescriptor].stream() + + when: + entityDescriptorFilesScheduledTasks.removeDanglingEntityDescriptorFiles() + + then: + def files = new File(tempPath, file) + files.size() == 0 + } + + def cleanup() { + directory.deleteDir() + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index 5e4a2d0a0..396b3df5b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -1,8 +1,20 @@ package edu.internet2.tier.shibboleth.admin.ui.service import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.* +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.XSAny +import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.AssertionConsumerServiceRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ContactRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.LogoutEndpointRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import org.springframework.boot.test.json.JacksonTester import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input @@ -11,6 +23,9 @@ import org.xmlunit.diff.ElementSelectors import spock.lang.Specification class JPAEntityDescriptorServiceImplTests extends Specification { + + def testObjectGenerator + OpenSamlObjects openSamlObjects = new OpenSamlObjects().with { init() it @@ -20,8 +35,12 @@ class JPAEntityDescriptorServiceImplTests extends Specification { JacksonTester jacksonTester + RandomGenerator generator + def setup() { JacksonTester.initFields(this, new ObjectMapper()) + generator = new RandomGenerator() + testObjectGenerator = new TestObjectGenerator() } @@ -589,4 +608,129 @@ class JPAEntityDescriptorServiceImplTests extends Specification { assert descriptor.getSPSSODescriptor('').getKeyDescriptors().size() == 1 assert descriptor.getSPSSODescriptor('').getKeyDescriptors()[0].getUse() == null } + + def "createAttributeWithBooleanValue properly adds booleans to attributes"() { + given: + def expectedName = "someName" + def expectedFriendlyName = "someFriendlyName" + def randomBoolean = generator.randomBoolean() + + when: + def attribute = service.createAttributeWithBooleanValue(expectedName, expectedFriendlyName, randomBoolean) + + then: + expectedName == attribute.getName() + expectedFriendlyName == attribute.getFriendlyName() + attribute.getAttributeValues().size() == 1 + attribute.getAttributeValues().get(0) instanceof XSBoolean + Boolean.parseBoolean(((XSBoolean)attribute.getAttributeValues().get(0)).getStoredValue()) == randomBoolean + + where: + i << (1..5) + } + + def "createAttributeWithArbitraryValues properly adds additional attributes"() { + given: + def expectedName = "someName" + def expectedFriendlyName = "someFriendlyName" + def attributesArray = [] + for (int index = 0; index < testRunIndex; index++) { + attributesArray.add("additionalAttributes" + index) + } + + + when: + def attribute = service.createAttributeWithArbitraryValues(expectedName, + expectedFriendlyName, + attributesArray) + + then: + expectedName == attribute.getName() + expectedFriendlyName == attribute.getFriendlyName() + attribute.getAttributeValues().size() == testRunIndex + for (int index = 0; index < testRunIndex; index++) { + attribute.getAttributeValues().get(index) instanceof XSAny + ((XSAny)attribute.getAttributeValues().get(index)).getTextContent() == "additionalAttributes" + index + } + + where: + testRunIndex << (1..5) + } + + def "createAttributeWithArbitraryValues adds no attributes when passed no attributes"() { + given: + def expectedName = "someName" + def expectedFriendlyName = "someFriendlyName" + + when: + def attribute = service.createAttributeWithArbitraryValues(expectedName, expectedFriendlyName) + + then: + expectedName == attribute.getName() + expectedFriendlyName == attribute.getFriendlyName() + attribute.getAttributeValues().size() == 0 + } + + def "createAttributeWithArbitraryValues doesn't explode when passed a list of strings"() { + given: + def expectedName = "someName" + def expectedFriendlyName = "someFriendlyName" + List attributesList = new ArrayList() + for (int index = 0; index < testRunIndex; index++) { + attributesList.add("additionalAttributes" + index) + } + + when: + def attribute = service.createAttributeWithArbitraryValues(expectedName, + expectedFriendlyName, + attributesList) + + then: + expectedName == attribute.getName() + expectedFriendlyName == attribute.getFriendlyName() + attribute.getAttributeValues().size() == testRunIndex + for (int index = 0; index < testRunIndex; index++) { + attribute.getAttributeValues().get(index) instanceof XSAny + ((XSAny)attribute.getAttributeValues().get(index)).getTextContent() == "additionalAttributes" + index + } + + where: + testRunIndex << (1..5) + } + + def "updateDescriptorFromRepresentation throws expected exception"() { + given: + def randomEntityDescriptor = generateRandomEntityDescriptor() + def entityDescriptorRepresentation = service.createRepresentationFromDescriptor(randomEntityDescriptor) + + when: + service.updateDescriptorFromRepresentation(randomEntityDescriptor, entityDescriptorRepresentation) + + then: + thrown UnsupportedOperationException + } + + def "createRepresentationFromDescriptor creates a representation containing a version that is a hash of the original object"() { + given: + def entityDescriptor = testObjectGenerator.buildEntityDescriptor() + def expectedVersion = entityDescriptor.hashCode() + + when: + def representation = service.createRepresentationFromDescriptor(entityDescriptor) + + then: + def actualVersion = representation.version + expectedVersion == actualVersion + } + + EntityDescriptor generateRandomEntityDescriptor() { + EntityDescriptor ed = new EntityDescriptor() + + ed.setEntityID(generator.randomId()) + ed.setServiceProviderName(generator.randomString(10)) + ed.setServiceEnabled(generator.randomBoolean()) + ed.setResourceId(generator.randomId()) + + return ed + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy new file mode 100644 index 000000000..8e212bb90 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImplTests.groovy @@ -0,0 +1,92 @@ +package edu.internet2.tier.shibboleth.admin.ui.service + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.XSString +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility +import edu.internet2.tier.shibboleth.admin.util.MDDCConstants +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, MetadataResolverConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class JPAEntityServiceImplTests extends Specification { + + @Autowired + OpenSamlObjects openSamlObjects + + @Autowired + AttributeUtility attributeUtility; + + def randomGenerator + def testObjectGenerator + + def service + + def setup() { + service = new JPAEntityServiceImpl(openSamlObjects) + service.attributeUtility = attributeUtility + + randomGenerator = new RandomGenerator() + testObjectGenerator = new TestObjectGenerator(attributeUtility) + } + + def "getAttributeListFromEntityRepresentation builds an appropriate attribute list"() { + given: + def representation = new EntityDescriptorRepresentation() + representation.setAttributeRelease(randomGenerator.randomStringList()) + representation.setRelyingPartyOverrides(testObjectGenerator.buildRelyingPartyOverridesRepresentation()) + + when: + def result = service.getAttributeListFromEntityRepresentation(representation) + + then: + //TODO: Similar to JPAFilterServiceImplTests, should we do a more thorough test or is checking the count sufficient? + result.size == 1 + TestHelpers.determineCountOfAttributesFromRelyingPartyOverrides(representation.getRelyingPartyOverrides()) + } + + def "getAttributeFromAttributeReleaseList builds an attribute properly"() { + given: + def listOfStrings = randomGenerator.randomStringList() + + def expectedAttributeName = MDDCConstants.RELEASE_ATTRIBUTES + def expectedNamespaceURI = "urn:oasis:names:tc:SAML:2.0:assertion" + def expectedElementLocalName = "AttributeValue" + def expectedNamespacePrefix = "saml2" + def expectedSchemaTypeNamespaceURI= "http://www.w3.org/2001/XMLSchema" + def expectedSchemaTypeElementLocalName = "string" + def expectedSchemaTypeNamespacePrefix = "xsd" + + when: + def result = service.getAttributeFromAttributeReleaseList(listOfStrings) + + then: + result.name == expectedAttributeName + result.attributeValues.size == listOfStrings.size + result.attributeValues.each { + listOfStrings.contains(it.value) + it.namespaceURI == expectedNamespaceURI + it.elementLocalName == expectedElementLocalName + it.namespacePrefix == expectedNamespacePrefix + it.schemaTypeNamespaceURI == expectedSchemaTypeNamespaceURI + it.schemaTypeElementLocalName == expectedSchemaTypeElementLocalName + it.schemaTypeNamespacePrefix == expectedSchemaTypeNamespacePrefix + } + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy index d09e04d1f..069555ce7 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImplTests.groovy @@ -3,12 +3,10 @@ package edu.internet2.tier.shibboleth.admin.ui.service import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.MetadataResolverConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator +import edu.internet2.tier.shibboleth.admin.ui.util.TestHelpers import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import org.apache.commons.lang.StringUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -55,7 +53,9 @@ class JPAFilterServiceImplTests extends Specification { //complicated test. Testing size is fairly useful, but it forces us to assume that the attributes are what they //should be. Maybe testing that the attributes are what they should be should be done in a unit test for the //actual method that builds the attributes list? - result.getAttributes().size() == determineCountOfAttributesFromRelyingPartyOverrides(representation) + result.getAttributes().size() == + representation.getAttributeRelease().size() + + TestHelpers.determineCountOfAttributesFromRelyingPartyOverrides(representation.getRelyingPartyOverrides()) result.entityAttributesFilterTarget.value == representation.filterTarget.value result.entityAttributesFilterTarget.entityAttributesFilterTargetType.toString() == representation.filterTarget.type @@ -72,29 +72,14 @@ class JPAFilterServiceImplTests extends Specification { result.id == filter.resourceId result.filterName == filter.name result.filterEnabled == filter.filterEnabled + result.version == filter.hashCode() //TODO? See note above, same question. - determineCountOfAttributesFromRelyingPartyOverrides(result) == filter.getAttributes().size() + result.getAttributeRelease().size() + + TestHelpers.determineCountOfAttributesFromRelyingPartyOverrides(result.getRelyingPartyOverrides()) == + filter.getAttributes().size() result.filterTarget.type == filter.entityAttributesFilterTarget.entityAttributesFilterTargetType.toString() result.filterTarget.value == filter.entityAttributesFilterTarget.value } - - int determineCountOfAttributesFromRelyingPartyOverrides(FilterRepresentation representation) { - int count = 0 - - count += representation.getAttributeRelease().size() - RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation = representation.getRelyingPartyOverrides() - count += relyingPartyOverridesRepresentation.authenticationMethods.size() != 0 ? 1 : 0 - count += relyingPartyOverridesRepresentation.dontSignResponse ? 1 : 0 - count += relyingPartyOverridesRepresentation.ignoreAuthenticationMethod ? 1 : 0 - count += relyingPartyOverridesRepresentation.nameIdFormats.size() != 0 ? 1 : 0 - count += relyingPartyOverridesRepresentation.omitNotBefore ? 1 : 0 - count += relyingPartyOverridesRepresentation.signAssertion ? 1 : 0 - count += relyingPartyOverridesRepresentation.turnOffEncryption ? 1 : 0 - count += relyingPartyOverridesRepresentation.useSha ? 1 : 0 - count += StringUtils.isNotBlank(relyingPartyOverridesRepresentation.responderId) ? 1 : 0 - - return count - } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImplTests.groovy index 80db7c8e5..0ceaaa82e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterTargetServiceImplTests.groovy @@ -42,5 +42,6 @@ class JPAFilterTargetServiceImplTests extends Specification { then: results.value == filterTarget.value results.type == filterTarget.entityAttributesFilterTargetType.toString() + results.version == filterTarget.hashCode() } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/RandomGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/RandomGenerator.groovy index 99e365705..0dbee4b0b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/RandomGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/RandomGenerator.groovy @@ -2,6 +2,9 @@ package edu.internet2.tier.shibboleth.admin.ui.util import org.apache.commons.lang3.RandomStringUtils +import java.time.LocalDateTime +import java.time.ZoneId + /** * @author Bill Smith (wsmith@unicon.net) */ @@ -39,6 +42,10 @@ class RandomGenerator { } } + int randomInt() { + return rand.nextInt() + } + Date randomDate() { return randomDate(randomBoolean()) } @@ -55,6 +62,11 @@ class RandomGenerator { return new Date(time) } + LocalDateTime randomLocalDateTime() { + def date = randomDate() + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()) + } + boolean randomBoolean() { return rand.nextBoolean() } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy new file mode 100644 index 000000000..1a6a085e2 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestHelpers.groovy @@ -0,0 +1,25 @@ +package edu.internet2.tier.shibboleth.admin.ui.util + +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation +import org.apache.commons.lang.StringUtils + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +class TestHelpers { + static int determineCountOfAttributesFromRelyingPartyOverrides(RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation) { + int count = 0 + + count += relyingPartyOverridesRepresentation.authenticationMethods.size() != 0 ? 1 : 0 + count += relyingPartyOverridesRepresentation.dontSignResponse ? 1 : 0 + count += relyingPartyOverridesRepresentation.ignoreAuthenticationMethod ? 1 : 0 + count += relyingPartyOverridesRepresentation.nameIdFormats.size() != 0 ? 1 : 0 + count += relyingPartyOverridesRepresentation.omitNotBefore ? 1 : 0 + count += relyingPartyOverridesRepresentation.signAssertion ? 1 : 0 + count += relyingPartyOverridesRepresentation.turnOffEncryption ? 1 : 0 + count += relyingPartyOverridesRepresentation.useSha ? 1 : 0 + count += StringUtils.isNotBlank(relyingPartyOverridesRepresentation.responderId) ? 1 : 0 + + return count + } +} 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 aaf9c23d2..302bdac95 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -1,13 +1,20 @@ package edu.internet2.tier.shibboleth.admin.ui.util import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute +import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributesFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.LocalizedName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationDisplayName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationURL import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterTargetRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import edu.internet2.tier.shibboleth.admin.util.MDDCConstants +import org.opensaml.saml.saml2.metadata.Organization /** * @author Bill Smith (wsmith@unicon.net) @@ -130,4 +137,79 @@ class TestObjectGenerator { EntityAttributesFilterTarget.EntityAttributesFilterTargetType randomFilterTargetType() { EntityAttributesFilterTarget.EntityAttributesFilterTargetType.values()[generator.randomInt(0, 2)] } + + EntityDescriptor buildEntityDescriptor() { + EntityDescriptor entityDescriptor = new EntityDescriptor() + + entityDescriptor.setID(generator.randomId()) + entityDescriptor.setEntityID(generator.randomId()) + entityDescriptor.setServiceProviderName(generator.randomString(20)) + entityDescriptor.setServiceEnabled(generator.randomBoolean()) + entityDescriptor.setResourceId(generator.randomId()) + entityDescriptor.setOrganization(buildOrganization()) + entityDescriptor.addContactPerson(buildContactPerson()) + + //TODO: Implement these if we ever start setting them elsewhere + //entityDescriptor.setRoleDescriptors(buildRoleDescriptors()) + //entityDescriptor.setAdditionalMetadataLocations(buildAdditionalMetadataLocations()) + //entityDescriptor.setAuthnAuthorityDescriptor(buildAuthnAuthorityDescriptor()) + //entityDescriptor.setAttributeAuthorityDescriptor(buildAttributeAuthorityDescriptor()) + //entityDescriptor.setPdpDescriptor(buildPdpDescriptor()) + //entityDescriptor.setAffiliationDescriptor(buildAffiliationDescriptor()) + + entityDescriptor.setCreatedBy(generator.randomString(10)) + entityDescriptor.setCreatedDate(generator.randomLocalDateTime()) + + return entityDescriptor + } + + Organization buildOrganization() { + Organization organization = new edu.internet2.tier.shibboleth.admin.ui.domain.Organization() + + organization.setNamespaceURI(generator.randomString(20)) + organization.setElementLocalName(generator.randomString(20)) + organization.setNamespacePrefix(generator.randomString(5)) + + organization.setOrganizationNames(buildListOfTypeWithValues(OrganizationName.class, generator.randomInt(1, 10))) + organization.setOrganizationDisplayNames(buildListOfTypeWithValues(OrganizationDisplayName.class, generator.randomInt(1, 10))) + organization.setOrganizationURLs(buildListOfTypeWithValues(OrganizationURL.class, generator.randomInt(1, 10))) + + //TODO: Implement these if we ever start setting them elsewhere + //organization.setExtensions(buildExtensions()) + + return organization + } + + ContactPerson buildContactPerson() { + ContactPerson contactPerson = new ContactPerson(); + + contactPerson.setNamespaceURI(generator.randomString(20)) + contactPerson.setElementLocalName(generator.randomString(20)) + contactPerson.setNamespacePrefix(generator.randomString(5)) + + return contactPerson + } + + /** + * This method takes a type and a size and builds a List of that size containing objects of that type. This is + * intended to be used with things that extend LocalizedName such as {@link OrganizationName}, {@link OrganizationDisplayName}, + * or with {@link OrganizationURL}s (really, a class that has a setValue() method). + * + * @param type the type of list to generate + * @param listSize the number of instances of that type to generate and add to the list + * @return a list of the specified size containing objects of the specified type + */ + private List buildListOfTypeWithValues(Class type, int listSize) { + List list = [] + listSize.times { + T newItemOfType = type.newInstance() + if (newItemOfType instanceof LocalizedName) { + newItemOfType.value = generator.randomString(10) + } else if (newItemOfType instanceof OrganizationURL) { + newItemOfType.value = generator.randomString(10) + } + list.add(newItemOfType) + } + return list + } } diff --git a/misc/Shibboleth Management.bmpr b/misc/Shibboleth Management.bmpr index b00eb3111..d6138ceee 100644 Binary files a/misc/Shibboleth Management.bmpr and b/misc/Shibboleth Management.bmpr differ diff --git a/ui/package-lock.json b/ui/package-lock.json index 5ff0025d3..82dfa6612 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1716,7 +1716,7 @@ }, "compression": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.2.tgz", + "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", "dev": true, "requires": { diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 1a3265ba6..27996e220 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -4,7 +4,7 @@ Shibboleth Logo - Click to be directed to www.shibboleth.net Shibboleth - Metadata Provider Management + Metadata Source Management diff --git a/ui/src/app/domain/entity/filter.ts b/ui/src/app/domain/entity/filter.ts index 03860fcdd..58d386855 100644 --- a/ui/src/app/domain/entity/filter.ts +++ b/ui/src/app/domain/entity/filter.ts @@ -6,6 +6,7 @@ export class Filter implements MetadataFilter { id = ''; createdDate?: string; modifiedDate?: string; + version: string; filterName = ''; filterEnabled = false; diff --git a/ui/src/app/domain/entity/provider.ts b/ui/src/app/domain/entity/provider.ts index 4adbd32e1..f9bbb3692 100644 --- a/ui/src/app/domain/entity/provider.ts +++ b/ui/src/app/domain/entity/provider.ts @@ -16,6 +16,7 @@ export class Provider implements MetadataProvider { id = ''; createdDate?: string; modifiedDate?: string; + version: string; entityId = ''; serviceProviderName = ''; diff --git a/ui/src/app/domain/model/mdui.ts b/ui/src/app/domain/model/mdui.ts index d31287b56..6e309f3f1 100644 --- a/ui/src/app/domain/model/mdui.ts +++ b/ui/src/app/domain/model/mdui.ts @@ -6,4 +6,4 @@ export interface MDUI { logoHeight?: number; logoWidth?: number; description?: string; -} \ No newline at end of file +} diff --git a/ui/src/app/domain/model/metadata-base.ts b/ui/src/app/domain/model/metadata-base.ts index e3b75e1ca..6a523f16a 100644 --- a/ui/src/app/domain/model/metadata-base.ts +++ b/ui/src/app/domain/model/metadata-base.ts @@ -10,6 +10,7 @@ export interface MetadataBase { id?: string; createdDate?: string; modifiedDate?: string; + version: string; name: string; enabled: boolean; diff --git a/ui/src/app/domain/reducer/filter-collection.reducer.spec.ts b/ui/src/app/domain/reducer/filter-collection.reducer.spec.ts index 0fdfa326a..687c54995 100644 --- a/ui/src/app/domain/reducer/filter-collection.reducer.spec.ts +++ b/ui/src/app/domain/reducer/filter-collection.reducer.spec.ts @@ -1,6 +1,14 @@ import { reducer } from './filter-collection.reducer'; import * as fromFilter from './filter-collection.reducer'; import * as actions from '../action/filter-collection.action'; +import { + FilterCollectionActionsUnion, + FilterCollectionActionTypes, + LoadFilterSuccess, + UpdateFilterSuccess, + SelectFilter +} from '../action/filter-collection.action'; +import { Filter } from '../entity/filter'; const snapshot: fromFilter.FilterCollectionState = { ids: [], @@ -17,4 +25,39 @@ describe('Filter Reducer', () => { expect(result).toEqual(snapshot); }); }); + + describe(`${FilterCollectionActionTypes.SELECT}`, () => { + it('should set the selected id in the store', () => { + const selectedFilterId = 'foo'; + const action = new SelectFilter(selectedFilterId); + const result = reducer(snapshot, action); + expect(result).toEqual({...snapshot, selectedFilterId}); + }); + }); + + describe(`${FilterCollectionActionTypes.LOAD_FILTER_SUCCESS}`, () => { + it('should add the loaded filters to the collection', () => { + spyOn(fromFilter.adapter, 'addMany').and.callThrough(); + const filters = [ + new Filter({ id: 'foo', createdDate: new Date().toLocaleDateString() }), + new Filter({ id: 'bar', createdDate: new Date().toLocaleDateString() }) + ]; + const action = new LoadFilterSuccess(filters); + const result = reducer(snapshot, action); + expect(fromFilter.adapter.addMany).toHaveBeenCalled(); + }); + }); + + describe(`${FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS}`, () => { + it('should add the loaded filters to the collection', () => { + spyOn(fromFilter.adapter, 'updateOne').and.callThrough(); + const update = { + id: 'foo', + changes: new Filter({ id: 'foo', filterName: 'bar', createdDate: new Date().toLocaleDateString() }), + }; + const action = new UpdateFilterSuccess(update); + const result = reducer(snapshot, action); + expect(fromFilter.adapter.updateOne).toHaveBeenCalled(); + }); + }); }); diff --git a/ui/src/app/domain/service/entity-descriptor.service.ts b/ui/src/app/domain/service/entity-descriptor.service.ts index ec0b38b46..b42711431 100644 --- a/ui/src/app/domain/service/entity-descriptor.service.ts +++ b/ui/src/app/domain/service/entity-descriptor.service.ts @@ -39,9 +39,6 @@ export class EntityDescriptorService { } save(provider: MetadataProvider): Observable { - if (!environment.production) { - // console.log(JSON.stringify(provider)); - } return this.http.post(`${this.base}${this.endpoint}`, provider); } diff --git a/ui/src/app/edit-provider/component/wizard-nav.component.html b/ui/src/app/edit-provider/component/wizard-nav.component.html index 079448ed4..875b41de0 100644 --- a/ui/src/app/edit-provider/component/wizard-nav.component.html +++ b/ui/src/app/edit-provider/component/wizard-nav.component.html @@ -29,8 +29,8 @@

+ + + + + + + diff --git a/ui/src/app/metadata-provider/container/confirm-copy.component.scss b/ui/src/app/metadata-provider/container/confirm-copy.component.scss new file mode 100644 index 000000000..9b93e80de --- /dev/null +++ b/ui/src/app/metadata-provider/container/confirm-copy.component.scss @@ -0,0 +1,5 @@ +:host { + .nav.nav-wizard .nav-item .nav-link.btn.save { + min-width: 180px; + } +} \ No newline at end of file diff --git a/ui/src/app/metadata-provider/container/confirm-copy.component.spec.ts b/ui/src/app/metadata-provider/container/confirm-copy.component.spec.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata-provider/container/confirm-copy.component.ts b/ui/src/app/metadata-provider/container/confirm-copy.component.ts new file mode 100644 index 000000000..fdf0956b9 --- /dev/null +++ b/ui/src/app/metadata-provider/container/confirm-copy.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { Observable } from 'rxjs/Observable'; + +import * as fromProvider from '../reducer'; +import { MetadataProvider } from '../../domain/domain.type'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { ProviderValueEmitter } from '../../domain/service/provider-change-emitter.service'; +import { UpdateProviderCopy } from '../action/copy.action'; +import { map, take } from 'rxjs/operators'; +import { AddProviderRequest } from '../../domain/action/provider-collection.action'; + +@Component({ + selector: 'confirm-copy-page', + templateUrl: './confirm-copy.component.html', + styleUrls: ['./confirm-copy.component.scss'] +}) +export class ConfirmCopyComponent { + + copy$: Observable; + values$: Observable; + saving$: Observable; + + provider: MetadataProvider; + + constructor( + private store: Store, + private valueEmitter: ProviderValueEmitter + ) { + this.copy$ = this.store.select(fromProvider.getCopy); + this.saving$ = this.store.select(fromProvider.getSaving); + + this.values$ = this.copy$.pipe(take(1)); + this.valueEmitter.changeEmitted$.subscribe(changes => this.store.dispatch(new UpdateProviderCopy(changes))); + + this.copy$.subscribe(p => this.provider = p); + } + + onSave(provider: MetadataProvider): void { + this.store.dispatch(new AddProviderRequest(provider)); + } +} /* istanbul ignore next */ diff --git a/ui/src/app/metadata-provider/container/copy-provider.component.html b/ui/src/app/metadata-provider/container/copy-provider.component.html new file mode 100644 index 000000000..07bfe98f1 --- /dev/null +++ b/ui/src/app/metadata-provider/container/copy-provider.component.html @@ -0,0 +1,75 @@ +
+ +
+
+ + + + + + + + +
+
+ + + + + Service Provider Name is required + + +
+
+ + + + + Entity ID is required + + + + Entity ID must be unique + +
+ +
+
\ No newline at end of file diff --git a/ui/src/app/metadata-provider/container/copy-provider.component.spec.ts b/ui/src/app/metadata-provider/container/copy-provider.component.spec.ts new file mode 100644 index 000000000..54981361c --- /dev/null +++ b/ui/src/app/metadata-provider/container/copy-provider.component.spec.ts @@ -0,0 +1,48 @@ +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule, Store, combineReducers } from '@ngrx/store'; +import { NewProviderComponent } from './new-provider.component'; +import * as fromCollections from '../../domain/reducer'; +import * as fromProvider from '../reducer'; +import { CopyProviderComponent } from './copy-provider.component'; +import { SharedModule } from '../../shared/shared.module'; +import { NavigatorService } from '../../core/service/navigator.service'; + +describe('Copy Provider Page', () => { + let fixture: ComponentFixture; + let store: Store; + let instance: CopyProviderComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + StoreModule.forRoot({ + collections: combineReducers(fromCollections.reducers), + provider: combineReducers(fromProvider.reducers) + }), + ReactiveFormsModule, + SharedModule + ], + declarations: [ + CopyProviderComponent + ], + providers: [ + NavigatorService + ] + }); + + fixture = TestBed.createComponent(CopyProviderComponent); + instance = fixture.componentInstance; + store = TestBed.get(Store); + + spyOn(store, 'dispatch').and.callThrough(); + }); + + it('should compile', () => { + fixture.detectChanges(); + + expect(fixture).toBeDefined(); + }); +}); diff --git a/ui/src/app/metadata-provider/container/copy-provider.component.ts b/ui/src/app/metadata-provider/container/copy-provider.component.ts new file mode 100644 index 000000000..9c3ae4182 --- /dev/null +++ b/ui/src/app/metadata-provider/container/copy-provider.component.ts @@ -0,0 +1,69 @@ +import { + Component, + OnInit, + Output, + EventEmitter +} from '@angular/core'; +import { FormBuilder, FormGroup, FormControl, FormControlName, Validators, AbstractControl } from '@angular/forms'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { Store } from '@ngrx/store'; + +import { startWith, take } from 'rxjs/operators'; + +import { AddDraftRequest } from '../../domain/action/draft-collection.action'; +import { AddProviderRequest, UploadProviderRequest } from '../../domain/action/provider-collection.action'; +import * as fromCollections from '../../domain/reducer'; +import { EntityValidators } from '../../domain/service/entity-validators.service'; +import { SearchIds } from '../action/search.action'; +import * as fromProvider from '../reducer'; +import { Provider } from '../../domain/entity/provider'; +import { CreateProviderCopyRequest } from '../action/copy.action'; + + +@Component({ + selector: 'copy-provider-form', + templateUrl: './copy-provider.component.html' +}) +export class CopyProviderComponent implements OnInit { + @Output() save: EventEmitter = new EventEmitter(); + + providerForm: FormGroup; + ids$: Observable; + searchResults$: Observable; + + constructor( + private store: Store, + private fb: FormBuilder + ) { + this.ids$ = this.store.select(fromCollections.getAllEntityIds); + this.searchResults$ = this.store.select(fromProvider.getSearchResults); + } + + ngOnInit(): void { + this.providerForm = this.fb.group({ + serviceProviderName: ['', [Validators.required]], + entityId: ['', Validators.required, EntityValidators.createUniqueIdValidator(this.ids$)], + target: ['', [Validators.required], [EntityValidators.existsInCollection(this.ids$)]] + }); + + this.store.select(fromProvider.getAttributes) + .pipe(take(1)) + .subscribe(attrs => this.providerForm.setValue({ ...attrs })); + + this.providerForm + .get('target') + .valueChanges + .subscribe(val => { + this.store.dispatch(new SearchIds(val)); + }); + } + + next(): void { + this.store.dispatch(new CreateProviderCopyRequest({ + ...this.providerForm.value + })); + } + + updateOptions(query: string): void {} +} /* istanbul ignore next */ diff --git a/ui/src/app/metadata-provider/container/new-provider.component.html b/ui/src/app/metadata-provider/container/new-provider.component.html index f32d9a8ce..4ba004908 100644 --- a/ui/src/app/metadata-provider/container/new-provider.component.html +++ b/ui/src/app/metadata-provider/container/new-provider.component.html @@ -3,7 +3,7 @@
- Add a new metadata provider + Add a new metadata source
@@ -11,29 +11,52 @@

How are you adding the metadata information?


-
- -
-
-  or  -
-
- -
-
-
-
-
- - +
+
+
+ +
+
+  or  +
+
+ +
+
+  or  +
+
+ +
+
+
+
+ +
diff --git a/ui/src/app/metadata-provider/container/new-provider.component.scss b/ui/src/app/metadata-provider/container/new-provider.component.scss new file mode 100644 index 000000000..ac956e4e7 --- /dev/null +++ b/ui/src/app/metadata-provider/container/new-provider.component.scss @@ -0,0 +1,5 @@ +:host { + .provider-nav-option { + width: 160px; + } +} \ No newline at end of file diff --git a/ui/src/app/metadata-provider/container/new-provider.component.spec.ts b/ui/src/app/metadata-provider/container/new-provider.component.spec.ts index 3ac3a9e16..e81de97f1 100644 --- a/ui/src/app/metadata-provider/container/new-provider.component.spec.ts +++ b/ui/src/app/metadata-provider/container/new-provider.component.spec.ts @@ -1,16 +1,28 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterModule, Router, ActivatedRoute } from '@angular/router'; +import { APP_BASE_HREF } from '@angular/common'; + import { StoreModule, Store, combineReducers } from '@ngrx/store'; import { NewProviderComponent } from './new-provider.component'; -import * as fromCollections from '../../domain/reducer'; + import { BlankProviderComponent } from './blank-provider.component'; import { UploadProviderComponent } from './upload-provider.component'; +import { CopyProviderComponent } from './copy-provider.component'; +import { SharedModule } from '../../shared/shared.module'; +import { NavigatorService } from '../../core/service/navigator.service'; +import * as fromProvider from '../reducer'; +import * as fromCollections from '../../domain/reducer'; +import { RouterStub } from '../../../testing/router.stub'; +import { ActivatedRouteStub } from '../../../testing/activated-route.stub'; describe('New Provider Page', () => { let fixture: ComponentFixture; let store: Store; let instance: NewProviderComponent; + let activatedRoute: ActivatedRouteStub = new ActivatedRouteStub(); + activatedRoute.testParamMap = { id: 'foo' }; beforeEach(() => { TestBed.configureTestingModule({ @@ -18,14 +30,23 @@ describe('New Provider Page', () => { NoopAnimationsModule, StoreModule.forRoot({ collections: combineReducers(fromCollections.reducers), + provider: combineReducers(fromProvider.reducers) }), ReactiveFormsModule, + SharedModule, + RouterModule.forRoot([]) ], declarations: [ NewProviderComponent, BlankProviderComponent, - UploadProviderComponent + UploadProviderComponent, + CopyProviderComponent ], + providers: [ + NavigatorService, + { provide: ActivatedRoute, useValue: activatedRoute }, + { provide: APP_BASE_HREF, useValue: '/' } + ] }); fixture = TestBed.createComponent(NewProviderComponent); diff --git a/ui/src/app/metadata-provider/container/new-provider.component.ts b/ui/src/app/metadata-provider/container/new-provider.component.ts index 43457f430..4dee1bf02 100644 --- a/ui/src/app/metadata-provider/container/new-provider.component.ts +++ b/ui/src/app/metadata-provider/container/new-provider.component.ts @@ -1,62 +1,11 @@ -import { - Component, - OnChanges, - OnInit, - OnDestroy, - ElementRef, - ViewChildren -} from '@angular/core'; -import { FormBuilder, FormGroup, FormControl, FormControlName, Validators, AbstractControl } from '@angular/forms'; -import 'rxjs/add/observable/merge'; -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/observable/fromEvent'; -import 'rxjs/add/operator/distinctUntilChanged'; -import 'rxjs/add/operator/take'; -import { Observable } from 'rxjs/Observable'; -import { Subject } from 'rxjs/Subject'; -import { Store } from '@ngrx/store'; - -import { MetadataProvider } from '../../domain/model/metadata-provider'; -import { Provider } from '../../domain/entity/provider'; -import { AddDraftRequest } from '../../domain/action/draft-collection.action'; -import { AddProviderRequest, UploadProviderRequest, CreateProviderFromUrlRequest } from '../../domain/action/provider-collection.action'; -import * as fromCollections from '../../domain/reducer'; -import { EntityValidators } from '../../domain/service/entity-validators.service'; +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'new-provider-page', - templateUrl: './new-provider.component.html' + templateUrl: './new-provider.component.html', + styleUrls: ['./new-provider.component.scss'] }) -export class NewProviderComponent implements OnInit { - private ngUnsubscribe: Subject = new Subject(); - - readonly UPLOAD = Symbol('UPLOAD_FORM'); - readonly BLANK = Symbol('BLANK_FORM'); - - type: Symbol = this.BLANK; - - constructor( - private store: Store - ) { } - - ngOnInit(): void { - this.toggle(this.type); - } - - toggle(type: Symbol): void { - this.type = type; - } - - upload(uploadFile: { name: string, body: string }): void { - this.store.dispatch(new UploadProviderRequest(uploadFile)); - } - - createFromUrl(data: { name: string, url: string }): void { - this.store.dispatch(new CreateProviderFromUrlRequest(data)); - } - - next(provider: { entityId: string, serviceProviderName: string }): void { - const val: MetadataProvider = new Provider(provider); - this.store.dispatch(new AddDraftRequest(val)); - } +export class NewProviderComponent { + constructor() {} } /* istanbul ignore next */ diff --git a/ui/src/app/metadata-provider/container/upload-provider.component.html b/ui/src/app/metadata-provider/container/upload-provider.component.html index e070fa5f4..926e44088 100644 --- a/ui/src/app/metadata-provider/container/upload-provider.component.html +++ b/ui/src/app/metadata-provider/container/upload-provider.component.html @@ -8,8 +8,8 @@