From 0e37753d1e527c1aa30e4a7aad5debfc0d10e98c Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 12 Jun 2018 16:16:06 -0400 Subject: [PATCH] SHIBUI-522: refactor entity attributes filter persistance and representation conversion nuances --- .../MetadataResolverConfiguration.java | 54 +-------- .../controller/MetadataFiltersController.java | 45 +++++-- .../filters/EntityAttributesFilter.java | 1 + .../MetadataResolverRepositoryTest.groovy | 112 +++++++++++++++++- 4 files changed, 148 insertions(+), 64 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java index 51ba89b05..62a449922 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java @@ -47,61 +47,11 @@ public class MetadataResolverConfiguration { public MetadataResolver metadataResolver() throws ResolverException, ComponentInitializationException { ChainingMetadataResolver metadataResolver = new ChainingMetadataResolver(); metadataResolver.setId("chain"); - List resolvers = new ArrayList<>(); - - // TODO: remove this later when we allow for creation of arbitrary metadata resolvers - FileBackedHTTPMetadataResolver incommonMR = new FileBackedHTTPMetadataResolver(HttpClients.createMinimal(), "http://md.incommon.org/InCommon/InCommon-metadata.xml", "/tmp/incommonmd.xml"){ - @Override - protected void initMetadataResolver() throws ComponentInitializationException { - super.initMetadataResolver(); - - for (String entityId: this.getBackingStore().getIndexedDescriptors().keySet()) { - Document document = new Document(); - document.add(new StringField("id", entityId, Field.Store.YES)); - document.add(new TextField("content", entityId, Field.Store.YES)); // TODO: change entityId to be content of entity descriptor block - try { - indexWriter.addDocument(document); - } catch (IOException e) { - logger.error(e.getMessage(), e); - } - } - try { - indexWriter.commit(); - } catch (IOException e) { - throw new ComponentInitializationException(e); - } - } - - // TODO: this is probably not the best way to do this - @Nullable - @Override - public DateTime getLastRefresh() { - return null; - } - - // TODO: this is probably not the best way to do this - @Override - protected void processConditionalRetrievalHeaders(HttpResponse response) { - // let's do nothing 'cause we want to allow a refresh - } - }; - incommonMR.setId("incommonmd"); - incommonMR.setParserPool(openSamlObjects.getParserPool()); - incommonMR.setMetadataFilter(new MetadataFilterChain()); - incommonMR.initialize(); - - - resolvers.add(incommonMR); - - if (!metadataResolverRepository.findAll().iterator().hasNext()) { - edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr = new edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver(); - mr.setName("incommonmd"); - metadataResolverRepository.save(mr); - } - metadataResolver.setResolvers(resolvers); + metadataResolver.initialize(); + return metadataResolver; } } 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 e810f9380..7e1684fab 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 @@ -26,6 +26,7 @@ import java.net.URI; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; @RestController @RequestMapping("/api/MetadataResolver/{metadataResolverId}") @@ -64,23 +65,29 @@ public ResponseEntity create(@PathVariable String metadataResolverId, @Reques MetadataResolver metadataResolver = repository.findAll().iterator().next(); metadataResolver.getMetadataFilters().add(createdFilter); + //convert before saving into database + if(createdFilter instanceof EntityAttributesFilter) { + EntityAttributesFilter.class.cast(createdFilter).fromTransientRepresentation(); + } MetadataResolver persistedMr = repository.save(metadataResolver); // we reload the filters here after save metadataResolverService.reloadFilters(persistedMr.getName()); + MetadataFilter persistedFilter = + convertIntoTransientRepresentationIfNecessary(persistedMr.getMetadataFilters().stream(), createdFilter.getResourceId()); + return ResponseEntity .created(getResourceUriFor(persistedMr, createdFilter.getResourceId())) - .body(persistedMr.getMetadataFilters() - .stream() - .filter(filter -> filter.getResourceId().equals(createdFilter.getResourceId())) - .collect(Collectors.toList()) - .get(0)); // "There can be only one!!!" + .body(persistedFilter); } @PutMapping("/Filters/{resourceId}") - public ResponseEntity update(@PathVariable String metadataResolverId, @RequestBody MetadataFilter updatedFilter) { + public ResponseEntity update(@PathVariable String metadataResolverId, + @PathVariable String resourceId, + @RequestBody MetadataFilter updatedFilter) { + //TODO: replace with get by metadataResolverId once we have more than one MetadataResolver metadataResolver = repository.findAll().iterator().next(); @@ -105,14 +112,31 @@ public ResponseEntity update(@PathVariable String metadataResolverId, @Reques filterTobeUpdated.setFilterEnabled(updatedFilter.isFilterEnabled()); updateConcreteFilterTypeData(filterTobeUpdated, updatedFilter); + //convert before saving into database + if(filterTobeUpdated instanceof EntityAttributesFilter) { + EntityAttributesFilter.class.cast(filterTobeUpdated).fromTransientRepresentation(); + } + MetadataResolver persistedMr = repository.save(metadataResolver); metadataResolverService.reloadFilters(persistedMr.getName()); - return ResponseEntity.ok() - .body(persistedMr.getMetadataFilters().stream() - .filter(f -> f.getResourceId().equals(updatedFilter.getResourceId())) - .collect(Collectors.toList()).get(0)); + MetadataFilter persistedFilter = + convertIntoTransientRepresentationIfNecessary(persistedMr.getMetadataFilters().stream(), updatedFilter.getResourceId()); + + return ResponseEntity.ok().body(persistedFilter); + } + + private MetadataFilter convertIntoTransientRepresentationIfNecessary(Stream filters, final String filterResourceId) { + MetadataFilter persistedFilter = filters + .filter(f -> f.getResourceId().equals(filterResourceId)) + .collect(Collectors.toList()).get(0); + + //convert before saving into database + if(persistedFilter instanceof EntityAttributesFilter) { + EntityAttributesFilter.class.cast(persistedFilter).intoTransientRepresentation(); + } + return persistedFilter; } /** @@ -126,7 +150,6 @@ private void updateConcreteFilterTypeData(MetadataFilter filterToBeUpdated, Meta toFilter.setEntityAttributesFilterTarget(fromFilter.getEntityAttributesFilterTarget()); toFilter.setRelyingPartyOverrides(fromFilter.getRelyingPartyOverrides()); toFilter.setAttributeRelease(fromFilter.getAttributeRelease()); - toFilter.intoTransientRepresentation(); } else if(filterWithUpdatedData instanceof EntityRoleWhiteListFilter) { EntityRoleWhiteListFilter toFilter = EntityRoleWhiteListFilter.class.cast(filterToBeUpdated); 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 b2186d5ed..4d2e6ab84 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 @@ -60,6 +60,7 @@ public void intoTransientRepresentation() { @PrePersist @PreUpdate public void fromTransientRepresentation() { + this.attributes.clear(); List attributeList = new ArrayList<>(); attributeList.addAll(getAttributeListFromAttributeReleaseList(this.attributeRelease)); attributeList.addAll(getAttributeListFromRelyingPartyOverridesRepresentation(this.relyingPartyOverrides)); diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy index 7860d70b3..4ec3b06ed 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepositoryTest.groovy @@ -6,7 +6,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget - +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl @@ -92,4 +92,114 @@ class MetadataResolverRepositoryTest extends Specification { item1.hashCode() == item2.hashCode() } + def "persisting and performing transformation into transient representation for EntityAttributesFilter correctly"() { + when: + def mdr = new MetadataResolver().with { + it.name = "testme" + it + } + metadataResolverRepository.save(mdr) + + entityManager.flush() + entityManager.clear() + + //Simulate adding new filter with incoming transient attribute release value as done in MetadataFiltersController + def filter = new EntityAttributesFilter().with { + it.name = 'original' + it.resourceId = 'new-filter-UUID' + it.attributeRelease = ['attr-for-release'] + it.relyingPartyOverrides = new RelyingPartyOverridesRepresentation().with { + it.signAssertion = true + it + } + it + } + MetadataResolver metadataResolver = metadataResolverRepository.findAll().iterator().next() + + //convert before saving into database + filter.fromTransientRepresentation() + + metadataResolver.getMetadataFilters().add(filter) + MetadataResolver persistedMr = metadataResolverRepository.save(metadataResolver) + + entityManager.flush() + entityManager.clear() + + //This is how it's done in MetadataFiltersController POST/PUT to return the newly saved object back to UI layer + // We are checking here that the method responsible for converting persistent state into the + // transient state used by UI is performed correctly + EntityAttributesFilter persistedFilter = persistedMr.getMetadataFilters().find { + it.resourceId == filter.resourceId + } + //convert before returning to UI + persistedFilter.intoTransientRepresentation() + + then: + persistedFilter.name == 'original' + persistedFilter.attributeRelease.find() { + it == 'attr-for-release' + } + persistedFilter.attributes.findAll() { + it.attributeValues.find() { + it.value == 'attr-for-release' + } + } + persistedFilter.relyingPartyOverrides.signAssertion + + when: + entityManager.flush() + entityManager.clear() + //Now lets update our filter + filter = new EntityAttributesFilter().with { + it.name = 'updated' + it.resourceId = 'new-filter-UUID' + it.attributeRelease = ['attr-for-release', 'attr-for-release2'] + it.relyingPartyOverrides = new RelyingPartyOverridesRepresentation().with { + it.signAssertion = false + it + } + it + } + metadataResolver = metadataResolverRepository.findAll().iterator().next() + EntityAttributesFilter filterToBeUpdated = metadataResolver.metadataFilters.find() { + it.resourceId == filter.resourceId + } + filterToBeUpdated.name = filter.name + filterToBeUpdated.entityAttributesFilterTarget = filter.entityAttributesFilterTarget + filterToBeUpdated.relyingPartyOverrides = filter.relyingPartyOverrides + filterToBeUpdated.attributeRelease = filter.attributeRelease + + //convert before saving into database + filterToBeUpdated.fromTransientRepresentation() + entityManager.clear() + persistedMr = metadataResolverRepository.save(metadataResolver) + + + persistedFilter = persistedMr.getMetadataFilters().find { + it.resourceId == filter.resourceId + } + //convert before returning to UI + persistedFilter.intoTransientRepresentation() + + then: + persistedFilter.name == 'updated' + persistedFilter.attributeRelease.find() { + it == 'attr-for-release' + } + persistedFilter.attributeRelease.find() { + it == 'attr-for-release2' + } + persistedFilter.attributes.find() { + it.attributeValues.findAll() { + it.value == 'attr-for-release' + } + } + persistedFilter.attributes.find() { + it.attributeValues.findAll() { + it.value == 'attr-for-release2' + } + } + !persistedFilter.relyingPartyOverrides.signAssertion + } + }