From da4c142a20d616f3a65032db43cbed4b6c3878f0 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 25 May 2021 10:48:08 -0700 Subject: [PATCH 1/6] NOJIRA Correcting the header location path for responses from the controller --- .../CustomEntityAttributesDefinitionsController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java index 86aeb90ed..5c1fa9081 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java @@ -35,7 +35,7 @@ public ResponseEntity create(@RequestBody CustomEntityAttributeDefinition def if (cad != null) { HttpHeaders headers = new HttpHeaders(); - headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute").build().toUri()); + headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/entity/attribute").build().toUri()); return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.CONFLICT.value()), @@ -52,7 +52,7 @@ public ResponseEntity update(@RequestBody CustomEntityAttributeDefinition def CustomEntityAttributeDefinition cad = caService.find(definition.getName()); if (cad == null) { HttpHeaders headers = new HttpHeaders(); - headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute").build().toUri()); + headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/entity/attribute").build().toUri()); return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), @@ -76,7 +76,7 @@ public ResponseEntity getOne(@PathVariable String name) { if (cad == null) { HttpHeaders headers = new HttpHeaders(); headers.setLocation( - ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute/" + name).build().toUri()); + ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/entity/attribute/" + name).build().toUri()); return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), From 7229ea5488e9f2f45185af41749fe31473d9c31b Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 25 May 2021 11:02:21 -0700 Subject: [PATCH 2/6] NOJIRA Missed updating one header location path --- .../controller/CustomEntityAttributesDefinitionsController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java index 5c1fa9081..56183dfdb 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/CustomEntityAttributesDefinitionsController.java @@ -92,7 +92,7 @@ public ResponseEntity delete(@PathVariable String name) { if (cad == null) { HttpHeaders headers = new HttpHeaders(); headers.setLocation( - ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/attribute/" + name).build().toUri()); + ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/custom/entity/attribute/" + name).build().toUri()); return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), From 0d63979d17e9a64b3e8dea4a2d31df9daff3b750 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 25 May 2021 12:09:37 -0700 Subject: [PATCH 3/6] SHIBUI-1783 Refactored filters to move object specific code out of caller (which was checking by type to do type logic) to the actual subtypes themselves --- .../controller/MetadataFiltersController.java | 45 +------------------ .../filters/EntityAttributesFilter.java | 12 +++++ .../filters/EntityRoleWhiteListFilter.java | 28 +++++++++--- .../filters/IConcreteMetadataFilterType.java | 14 ++++++ .../ui/domain/filters/MetadataFilter.java | 2 +- .../ui/domain/filters/NameIdFormatFilter.java | 26 ++++++++--- .../filters/RequiredValidUntilFilter.java | 17 +++++-- .../filters/SignatureValidationFilter.java | 23 ++++++++-- 8 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/IConcreteMetadataFilterType.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java index 87c7a8bd3..33d4458b5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java @@ -125,7 +125,7 @@ public ResponseEntity update(@PathVariable String metadataResolverId, filterTobeUpdated.setName(updatedFilter.getName()); filterTobeUpdated.setFilterEnabled(updatedFilter.isFilterEnabled()); - updateConcreteFilterTypeData(filterTobeUpdated, updatedFilter); + updatedFilter.updateConcreteFilterTypeData(filterTobeUpdated); MetadataFilter persistedFilter = filterRepository.save(filterTobeUpdated); @@ -206,49 +206,6 @@ private MetadataFilter newlyPersistedFilter(Stream filters, fina return persistedFilter; } - /** - * Add else if instanceof block here for each concrete filter types we add in the future - */ - private void updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated, MetadataFilter filterWithUpdatedData) { - //TODO: Could we maybe use Dozer here before things get out of control? https://dozermapper.github.io - // Mapper mapper = new net.sf.dozer.Mapper(); // or autowire one - // mapper.map(fromFilter, toFilter); - if (filterWithUpdatedData instanceof EntityAttributesFilter) { - EntityAttributesFilter toFilter = EntityAttributesFilter.class.cast(filterToBeUpdated); - EntityAttributesFilter fromFilter = EntityAttributesFilter.class.cast(filterWithUpdatedData); - toFilter.setEntityAttributesFilterTarget(fromFilter.getEntityAttributesFilterTarget()); - toFilter.setRelyingPartyOverrides(fromFilter.getRelyingPartyOverrides()); - toFilter.setAttributeRelease(fromFilter.getAttributeRelease()); - } else if (filterWithUpdatedData instanceof EntityRoleWhiteListFilter) { - EntityRoleWhiteListFilter toFilter = EntityRoleWhiteListFilter.class.cast(filterToBeUpdated); - EntityRoleWhiteListFilter fromFilter = EntityRoleWhiteListFilter.class.cast(filterWithUpdatedData); - toFilter.setRemoveEmptyEntitiesDescriptors(fromFilter.getRemoveEmptyEntitiesDescriptors()); - toFilter.setRemoveRolelessEntityDescriptors(fromFilter.getRemoveRolelessEntityDescriptors()); - toFilter.setRetainedRoles(fromFilter.getRetainedRoles()); - } else if (filterWithUpdatedData instanceof SignatureValidationFilter) { - SignatureValidationFilter toFilter = SignatureValidationFilter.class.cast(filterToBeUpdated); - SignatureValidationFilter fromFilter = SignatureValidationFilter.class.cast(filterWithUpdatedData); - toFilter.setRequireSignedRoot(fromFilter.getRequireSignedRoot()); - toFilter.setCertificateFile(fromFilter.getCertificateFile()); - toFilter.setDefaultCriteriaRef(fromFilter.getDefaultCriteriaRef()); - toFilter.setSignaturePrevalidatorRef(fromFilter.getSignaturePrevalidatorRef()); - toFilter.setDynamicTrustedNamesStrategyRef(fromFilter.getDynamicTrustedNamesStrategyRef()); - toFilter.setTrustEngineRef(fromFilter.getTrustEngineRef()); - toFilter.setPublicKey(fromFilter.getPublicKey()); - } else if (filterWithUpdatedData instanceof RequiredValidUntilFilter) { - RequiredValidUntilFilter toFilter = RequiredValidUntilFilter.class.cast(filterToBeUpdated); - RequiredValidUntilFilter fromFilter = RequiredValidUntilFilter.class.cast(filterWithUpdatedData); - toFilter.setMaxValidityInterval(fromFilter.getMaxValidityInterval()); - } else if (filterWithUpdatedData instanceof NameIdFormatFilter) { - NameIdFormatFilter toFilter = NameIdFormatFilter.class.cast(filterToBeUpdated); - NameIdFormatFilter fromFilter = NameIdFormatFilter.class.cast(filterWithUpdatedData); - toFilter.setRemoveExistingFormats(fromFilter.getRemoveExistingFormats()); - toFilter.setFormats(fromFilter.getFormats()); - toFilter.setNameIdFormatFilterTarget(fromFilter.getNameIdFormatFilterTarget()); - } - //TODO: add other types of concrete filters update here - } - private static URI getResourceUriFor(MetadataResolver mr, String filterResourceId) { return ServletUriComponentsBuilder .fromCurrentServletMapping().path("/api/MetadataResolvers/") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index a3e0a6a93..83ce3a66e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -75,4 +75,16 @@ public void intoTransientRepresentation() { this.attributeRelease = getAttributeReleaseListFromAttributeList(this.attributes); this.relyingPartyOverrides = getRelyingPartyOverridesRepresentationFromAttributeList(this.attributes); } + + private EntityAttributesFilter updateConcreteFilterTypeData(EntityAttributesFilter filterToBeUpdated) { + filterToBeUpdated.setEntityAttributesFilterTarget(getEntityAttributesFilterTarget()); + filterToBeUpdated.setRelyingPartyOverrides(getRelyingPartyOverrides()); + filterToBeUpdated.setAttributeRelease(getAttributeRelease()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((EntityAttributesFilter) filterToBeUpdated); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java index 26c5e9976..0ac107dda 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java @@ -1,10 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.hibernate.envers.Audited; +import java.util.ArrayList; +import java.util.List; import javax.persistence.CollectionTable; import javax.persistence.Column; @@ -12,8 +9,13 @@ import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OrderColumn; -import java.util.ArrayList; -import java.util.List; + +import org.hibernate.envers.Audited; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; @Entity @EqualsAndHashCode(callSuper = true) @@ -36,4 +38,16 @@ public EntityRoleWhiteListFilter() { @Column(name="RETAINED_ROLE") @OrderColumn private List retainedRoles = new ArrayList<>(); + + private EntityRoleWhiteListFilter updateConcreteFilterTypeData(EntityRoleWhiteListFilter filterToBeUpdated) { + filterToBeUpdated.setRemoveEmptyEntitiesDescriptors(getRemoveEmptyEntitiesDescriptors()); + filterToBeUpdated.setRemoveRolelessEntityDescriptors(getRemoveRolelessEntityDescriptors()); + filterToBeUpdated.setRetainedRoles(getRetainedRoles()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((EntityRoleWhiteListFilter) filterToBeUpdated); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/IConcreteMetadataFilterType.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/IConcreteMetadataFilterType.java new file mode 100644 index 000000000..1f1448ff4 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/IConcreteMetadataFilterType.java @@ -0,0 +1,14 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +/** + * Concrete implementations of the MetadataFilter should implement the updateConcreteFilterTypeData signature to + * populate specific type data from the existing filter to the target filter. + */ +public interface IConcreteMetadataFilterType { + + /** + * @param filterToBeUpdated the target of the data + * @return the filterToBeUpdated with the updated information from the existing filter + */ + T updateConcreteFilterTypeData(T filterToBeUpdated); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java index b15058cd0..9363f5711 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java @@ -38,7 +38,7 @@ @JsonSubTypes.Type(value=NameIdFormatFilter.class, name="NameIDFormat")}) @Audited @AuditOverride(forClass = AbstractAuditable.class) -public class MetadataFilter extends AbstractAuditable { +public abstract class MetadataFilter extends AbstractAuditable implements IConcreteMetadataFilterType { @JsonProperty("@type") @Transient diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java index 157e5f766..906926916 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java @@ -1,17 +1,19 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; -import org.hibernate.envers.Audited; +import java.util.List; import javax.persistence.CascadeType; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; -import java.util.List; + +import org.hibernate.envers.Audited; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; @Entity @EqualsAndHashCode(callSuper = true) @@ -34,4 +36,16 @@ public NameIdFormatFilter() { @OneToOne(cascade = CascadeType.ALL) private NameIdFormatFilterTarget nameIdFormatFilterTarget; + private NameIdFormatFilter updateConcreteFilterTypeData(NameIdFormatFilter filterToBeUpdated) { + filterToBeUpdated.setRemoveExistingFormats(getRemoveExistingFormats()); + filterToBeUpdated.setFormats(getFormats()); + filterToBeUpdated.setNameIdFormatFilterTarget(getNameIdFormatFilterTarget()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((NameIdFormatFilter) filterToBeUpdated); + } + } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java index cda431408..80e6152aa 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java @@ -1,12 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; +import javax.persistence.Entity; + +import org.hibernate.envers.Audited; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.hibernate.envers.Audited; - -import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) @@ -25,4 +26,14 @@ public RequiredValidUntilFilter() { public boolean xmlShouldBeGenerated() { return (maxValidityInterval != null) && (!maxValidityInterval.equals("PT0S")); } + + private RequiredValidUntilFilter updateConcreteFilterTypeData(RequiredValidUntilFilter filterToBeUpdated) { + filterToBeUpdated.setMaxValidityInterval(getMaxValidityInterval()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((RequiredValidUntilFilter) filterToBeUpdated); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java index 5cf71225e..cf6aa3151 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java @@ -1,12 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; +import javax.persistence.Entity; + +import org.hibernate.envers.Audited; + import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.hibernate.envers.Audited; - -import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) @@ -37,4 +38,20 @@ public SignatureValidationFilter() { public boolean xmlShouldBeGenerated() { return requireSignedRoot; } + + private SignatureValidationFilter updateConcreteFilterTypeData(SignatureValidationFilter filterToBeUpdated) { + filterToBeUpdated.setRequireSignedRoot(getRequireSignedRoot()); + filterToBeUpdated.setCertificateFile(getCertificateFile()); + filterToBeUpdated.setDefaultCriteriaRef(getDefaultCriteriaRef()); + filterToBeUpdated.setSignaturePrevalidatorRef(getSignaturePrevalidatorRef()); + filterToBeUpdated.setDynamicTrustedNamesStrategyRef(getDynamicTrustedNamesStrategyRef()); + filterToBeUpdated.setTrustEngineRef(getTrustEngineRef()); + filterToBeUpdated.setPublicKey(getPublicKey()); + return filterToBeUpdated; + } + + @Override + public MetadataFilter updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated) { + return updateConcreteFilterTypeData((SignatureValidationFilter) filterToBeUpdated); + } } From 0cfbe25838ee1d3dc8943369d7ebdedf50595053 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Fri, 28 May 2021 08:33:41 -0700 Subject: [PATCH 4/6] SHIBUI-1783 Backend updates to saving filter to include any custom entity attributes --- .../CustomEntityAttributeDefinition.java | 2 + .../CustomEntityAttributeFilterValue.java | 43 +++++++++ .../filters/EntityAttributesFilter.java | 16 ++++ ...mEntityAttributeFilterValueRepository.java | 12 +++ .../repository/FilterRepositoryTests.groovy | 92 +++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/CustomEntityAttributeFilterValue.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomEntityAttributeDefinition.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomEntityAttributeDefinition.java index b2be0f503..d852ba5ea 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomEntityAttributeDefinition.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/CustomEntityAttributeDefinition.java @@ -3,12 +3,14 @@ import java.util.HashSet; import java.util.Set; +import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; import org.hibernate.envers.Audited; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/CustomEntityAttributeFilterValue.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/CustomEntityAttributeFilterValue.java new file mode 100644 index 000000000..ed6715631 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/CustomEntityAttributeFilterValue.java @@ -0,0 +1,43 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.filters; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.hibernate.envers.Audited; + +import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition; +import lombok.Getter; +import lombok.Setter; + + +@Entity(name = "custom_entity_attr_filter_value") +@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "filter_id", "custom_entity_attribute_name" }) }) +@Audited +// NOTE: lombok's toString and equals cause an infinite loop somewhere that causes stack overflows, so if we need impls, +// do it manually. Do not replace the Getter and Setter with @Data... +@Getter +@Setter +public class CustomEntityAttributeFilterValue { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "generated_id") + private Integer id; + + @ManyToOne + @JoinColumn(name = "filter_id", nullable = false) + EntityAttributesFilter entityAttributesFilter; + + @ManyToOne + @JoinColumn(name = "custom_entity_attribute_name", referencedColumnName = "name", nullable = false) + CustomEntityAttributeDefinition customEntityAttributeDefinition; + + @Column(name = "value", nullable = false) + String value; +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index 83ce3a66e..28926358f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -9,16 +9,21 @@ import org.hibernate.envers.Audited; import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import javax.persistence.PostLoad; import javax.persistence.Transient; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromAttributeReleaseList; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromRelyingPartyOverridesRepresentation; @@ -32,6 +37,7 @@ @ToString @Audited public class EntityAttributesFilter extends MetadataFilter { + private static final long serialVersionUID = 1L; public EntityAttributesFilter() { type = "EntityAttributes"; @@ -47,7 +53,16 @@ public EntityAttributesFilter() { @Transient private List attributeRelease = new ArrayList<>(); + + @JsonIgnore + @OneToMany(cascade = CascadeType.ALL, mappedBy = "entityAttributesFilter", orphanRemoval = true) + private Set customEntityAttributes = new HashSet<>(); + public void setCustomEntityAttributes (Set newValues) { + customEntityAttributes.clear(); + customEntityAttributes.addAll(newValues); + } + public void setAttributeRelease(List attributeRelease) { this.attributeRelease = attributeRelease; this.rebuildAttributes(); @@ -80,6 +95,7 @@ private EntityAttributesFilter updateConcreteFilterTypeData(EntityAttributesFilt filterToBeUpdated.setEntityAttributesFilterTarget(getEntityAttributesFilterTarget()); filterToBeUpdated.setRelyingPartyOverrides(getRelyingPartyOverrides()); filterToBeUpdated.setAttributeRelease(getAttributeRelease()); + filterToBeUpdated.setCustomEntityAttributes(customEntityAttributes); return filterToBeUpdated; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java new file mode 100644 index 000000000..72507d24a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttributeFilterValue; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; + +// Not entirely sure this is needed for the core, but it did make validation/unit testing a whole lot easier +public interface CustomEntityAttributeFilterValueRepository extends JpaRepository { + CustomEntityAttributeFilterValue findByEntityAttributesFilterAndCustomEntityAttributeDefinition(EntityAttributesFilter eaf , CustomEntityAttributeDefinition cead); +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy index d39cddc93..c8bbe9d74 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy @@ -5,6 +5,8 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfigurat import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttributeFilterValue import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import org.springframework.beans.factory.annotation.Autowired @@ -27,6 +29,12 @@ class FilterRepositoryTests extends Specification { @Autowired FilterRepository repositoryUnderTest + @Autowired + CustomEntityAttributeDefinitionRepository ceadRepo + + @Autowired + CustomEntityAttributeFilterValueRepository ceafvRepo + @Autowired EntityManager entityManager @@ -85,4 +93,88 @@ class FilterRepositoryTests extends Specification { persistedFilter.audId > 0L persistedFilter.formats.size() == 1 } + + def "FilterRepository + EntityAttributesFilter CRUD ops with custom entity attributes correctly"(){ + given: + def ca = new CustomEntityAttributeDefinition().with { + it.name = "ca-name" + it.attributeType = "STRING" + it.defaultValue = "foo" + it + } + ceadRepo.save(ca) + entityManager.flush() + entityManager.clear() + + def entityAttributesFilterJson = '''{ + "name": "EntityAttributes", + "resourceId": "29a5d409-562a-41cd-acee-e9b3d7098d05", + "filterEnabled": false, + "entityAttributesFilterTarget": { + "entityAttributesFilterTargetType": "CONDITION_SCRIPT", + "value": [ + "TwUuSOz5O6" + ] + }, + "attributeRelease": [ + "WbkhLQNI3m" + ], + "relyingPartyOverrides": { + "signAssertion": true, + "dontSignResponse": true, + "turnOffEncryption": true, + "useSha": true, + "ignoreAuthenticationMethod": false, + "omitNotBefore": true, + "responderId": null, + "nameIdFormats": [ + "xLenUFmCLn" + ], + "authenticationMethods": [] + }, + "@type": "EntityAttributes" + }''' + def filter = new ObjectMapper().readValue(entityAttributesFilterJson.bytes, EntityAttributesFilter.class) + def persistedFilter = repositoryUnderTest.save(filter) + entityManager.flush() + + def savedFilter = repositoryUnderTest.findByResourceId(persistedFilter.resourceId) + def saveEAD = ceadRepo.findByName("ca-name"); + + def ceafv = new CustomEntityAttributeFilterValue().with { + it.entityAttributesFilter = savedFilter + it.customEntityAttributeDefinition = saveEAD + it.value = "bar" + it + } + + def customEntityAttributes = new HashSet() + + when: + customEntityAttributes.add(ceafv) // nothing to do yet, just here to let us verify nothing in the CEAFV table in 'then' block + + then: + ((Set)ceafvRepo.findAll()).size() == 0 //nothing yet + ((EntityAttributesFilter)savedFilter).setCustomEntityAttributes(customEntityAttributes) + repositoryUnderTest.save(savedFilter) + entityManager.flush() + + then: + def listOfceafv = ceafvRepo.findAll() + listOfceafv.size() == 1 + + def ceafvFromDb = listOfceafv.get(0).asType(CustomEntityAttributeFilterValue) + ceafvFromDb.getEntityAttributesFilter().getResourceId().equals("29a5d409-562a-41cd-acee-e9b3d7098d05") + + def filterFromDb = (EntityAttributesFilter) repositoryUnderTest.findByResourceId("29a5d409-562a-41cd-acee-e9b3d7098d05") + filterFromDb.getCustomEntityAttributes().size() == 1 + + // now remove all + def emptySet = new HashSet() + filterFromDb.setCustomEntityAttributes(emptySet) + repositoryUnderTest.save(filterFromDb) + entityManager.flush() + + ceafvRepo.findAll().size() == 0 + } } From 837ce161bfa19c8d569dde106a81348b7041f46b Mon Sep 17 00:00:00 2001 From: chasegawa Date: Fri, 28 May 2021 11:43:00 -0700 Subject: [PATCH 5/6] SHIBUI-1798 Ensures that deleting a definition will also delete the filter references --- ...mEntityAttributeFilterValueRepository.java | 6 +- ...EntityAttributesDefinitionServiceImpl.java | 20 +++- .../ui/configuration/TestConfiguration.groovy | 2 + .../repository/FilterRepositoryTests.groovy | 95 ++++++++++++++++++- 4 files changed, 116 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java index 72507d24a..a6a7be164 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/CustomEntityAttributeFilterValueRepository.java @@ -1,12 +1,16 @@ package edu.internet2.tier.shibboleth.admin.ui.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttributeFilterValue; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; -// Not entirely sure this is needed for the core, but it did make validation/unit testing a whole lot easier public interface CustomEntityAttributeFilterValueRepository extends JpaRepository { + // Not entirely sure this is needed for the core, but it did make validation/unit testing a whole lot easier CustomEntityAttributeFilterValue findByEntityAttributesFilterAndCustomEntityAttributeDefinition(EntityAttributesFilter eaf , CustomEntityAttributeDefinition cead); + + List findAllByCustomEntityAttributeDefinition(CustomEntityAttributeDefinition definition); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/CustomEntityAttributesDefinitionServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/CustomEntityAttributesDefinitionServiceImpl.java index 506bf4e44..95f654386 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/CustomEntityAttributesDefinitionServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/CustomEntityAttributesDefinitionServiceImpl.java @@ -2,16 +2,26 @@ import java.util.List; +import javax.persistence.EntityManager; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttributeFilterValue; import edu.internet2.tier.shibboleth.admin.ui.repository.CustomEntityAttributeDefinitionRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.CustomEntityAttributeFilterValueRepository; @Service public class CustomEntityAttributesDefinitionServiceImpl implements CustomEntityAttributesDefinitionService { @Autowired private CustomEntityAttributeDefinitionRepository repository; + + @Autowired + CustomEntityAttributeFilterValueRepository customEntityAttributeFilterValueRepository; + + @Autowired + EntityManager entityManager; @Override public CustomEntityAttributeDefinition createOrUpdateDefinition(CustomEntityAttributeDefinition definition) { @@ -20,7 +30,15 @@ public CustomEntityAttributeDefinition createOrUpdateDefinition(CustomEntityAttr @Override public void deleteDefinition(CustomEntityAttributeDefinition definition) { - repository.delete(definition); + // must remove any CustomEntityAttributeFilterValues first to avoid integrity constraint issues + List customEntityValues = customEntityAttributeFilterValueRepository.findAllByCustomEntityAttributeDefinition(definition); + customEntityValues.forEach(value -> { + value.getEntityAttributesFilter().getCustomEntityAttributes().remove(value); + entityManager.remove(value); + customEntityAttributeFilterValueRepository.delete(value); + }); + CustomEntityAttributeDefinition entityToRemove = repository.findByName(definition.getName()); + repository.delete(entityToRemove); } @Override diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index 676547ffd..a18a4a7b1 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -19,6 +19,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration import org.springframework.core.io.ClassPathResource import org.springframework.data.domain.AuditorAware @@ -26,6 +27,7 @@ import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.JavaMailSenderImpl @Configuration +@ComponentScan("edu.internet2.tier.shibboleth.admin.ui.service") class TestConfiguration { @Autowired IndexWriterService indexWriterService diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy index c8bbe9d74..473f7d3c9 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy @@ -8,6 +8,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.CustomEntityAttributeDefinition import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttributeFilterValue import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionService import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan @@ -28,9 +29,9 @@ class FilterRepositoryTests extends Specification { @Autowired FilterRepository repositoryUnderTest - + @Autowired - CustomEntityAttributeDefinitionRepository ceadRepo + CustomEntityAttributesDefinitionService ceadService @Autowired CustomEntityAttributeFilterValueRepository ceafvRepo @@ -102,7 +103,7 @@ class FilterRepositoryTests extends Specification { it.defaultValue = "foo" it } - ceadRepo.save(ca) + ceadService.createOrUpdateDefinition(ca) entityManager.flush() entityManager.clear() @@ -139,7 +140,7 @@ class FilterRepositoryTests extends Specification { entityManager.flush() def savedFilter = repositoryUnderTest.findByResourceId(persistedFilter.resourceId) - def saveEAD = ceadRepo.findByName("ca-name"); + def saveEAD = ceadService.find("ca-name") def ceafv = new CustomEntityAttributeFilterValue().with { it.entityAttributesFilter = savedFilter @@ -176,5 +177,89 @@ class FilterRepositoryTests extends Specification { entityManager.flush() ceafvRepo.findAll().size() == 0 - } + } + + def "Delete custom entity attributes definition removes entries from filter correctly"(){ + given: + def ca = new CustomEntityAttributeDefinition().with { + it.name = "ca-name" + it.attributeType = "STRING" + it.defaultValue = "foo" + it + } + ceadService.createOrUpdateDefinition(ca) + entityManager.flush() + entityManager.clear() + + def entityAttributesFilterJson = '''{ + "name": "EntityAttributes", + "resourceId": "29a5d409-562a-41cd-acee-e9b3d7098d05", + "filterEnabled": false, + "entityAttributesFilterTarget": { + "entityAttributesFilterTargetType": "CONDITION_SCRIPT", + "value": [ + "TwUuSOz5O6" + ] + }, + "attributeRelease": [ + "WbkhLQNI3m" + ], + "relyingPartyOverrides": { + "signAssertion": true, + "dontSignResponse": true, + "turnOffEncryption": true, + "useSha": true, + "ignoreAuthenticationMethod": false, + "omitNotBefore": true, + "responderId": null, + "nameIdFormats": [ + "xLenUFmCLn" + ], + "authenticationMethods": [] + }, + "@type": "EntityAttributes" + }''' + def filter = new ObjectMapper().readValue(entityAttributesFilterJson.bytes, EntityAttributesFilter.class) + def persistedFilter = repositoryUnderTest.save(filter) + entityManager.flush() + + def savedFilter = repositoryUnderTest.findByResourceId(persistedFilter.resourceId) + def saveEAD = ceadService.find("ca-name"); + + def ceafv = new CustomEntityAttributeFilterValue().with { + it.entityAttributesFilter = savedFilter + it.customEntityAttributeDefinition = saveEAD + it.value = "bar" + it + } + + def customEntityAttributes = new HashSet() + + when: + customEntityAttributes.add(ceafv) // nothing to do yet, just here to let us verify nothing in the CEAFV table in 'then' block + + then: + ((Set)ceafvRepo.findAll()).size() == 0 //nothing yet + ((EntityAttributesFilter)savedFilter).setCustomEntityAttributes(customEntityAttributes) + repositoryUnderTest.save(savedFilter) + entityManager.flush() + + then: + def listOfceafv = ceafvRepo.findAll() + listOfceafv.size() == 1 + + def ceafvFromDb = listOfceafv.get(0).asType(CustomEntityAttributeFilterValue) + ceafvFromDb.getEntityAttributesFilter().getResourceId().equals("29a5d409-562a-41cd-acee-e9b3d7098d05") + + def filterFromDb = (EntityAttributesFilter) repositoryUnderTest.findByResourceId("29a5d409-562a-41cd-acee-e9b3d7098d05") + filterFromDb.getCustomEntityAttributes().size() == 1 + + // now remove the definition + ceadService.deleteDefinition(saveEAD) + entityManager.flush() + entityManager.clear() + + def filterFromDb2 = (EntityAttributesFilter)repositoryUnderTest.findByResourceId("29a5d409-562a-41cd-acee-e9b3d7098d05") + filterFromDb2.getCustomEntityAttributes().size() == 0 + } } From 1b18a3a97cb8cc54e68b67c5fe94d61f4bb4322d Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 1 Jun 2021 12:54:51 -0700 Subject: [PATCH 6/6] SHIBUI-1783 correcting unit test configuration --- .../ui/configuration/TestConfiguration.groovy | 1 - .../repository/FilterRepositoryTests.groovy | 20 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index a18a4a7b1..5e3c0ad1b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -27,7 +27,6 @@ import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.JavaMailSenderImpl @Configuration -@ComponentScan("edu.internet2.tier.shibboleth.admin.ui.service") class TestConfiguration { @Autowired IndexWriterService indexWriterService diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy index 473f7d3c9..48a042e5b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepositoryTests.groovy @@ -1,6 +1,16 @@ package edu.internet2.tier.shibboleth.admin.ui.repository +import javax.persistence.EntityManager + +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.context.annotation.ComponentScan +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration + import com.fasterxml.jackson.databind.ObjectMapper + import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -10,21 +20,13 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.CustomEntityAttribu import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionService import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.domain.EntityScan -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.test.context.ContextConfiguration import spock.lang.Specification -import javax.persistence.EntityManager - -import static edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator.* - @DataJpaTest @ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") +@ComponentScan("edu.internet2.tier.shibboleth.admin.ui.service") class FilterRepositoryTests extends Specification { @Autowired