Skip to content

Commit

Permalink
Merge branch 'master' into SHIBUI-518
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Smith committed Jun 15, 2018
2 parents 16c9874 + d0faa36 commit 22c7d0e
Show file tree
Hide file tree
Showing 45 changed files with 441 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetad
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;

import com.google.common.base.Predicate

import com.google.common.base.Predicate
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter


import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver

import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects

import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository
Expand All @@ -26,6 +34,7 @@ import org.opensaml.saml.saml2.core.Attribute
import org.opensaml.saml.saml2.metadata.EntityDescriptor

import org.springframework.beans.factory.annotation.Autowired

import org.w3c.dom.Document

@Slf4j
Expand Down Expand Up @@ -106,27 +115,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
'xsi:type': 'RequiredValidUntil',
'maxValidityInterval': 'P14D'
)
MetadataFilter(
'xsi:type': 'EntityRoleWhiteList'
) {
RetainedRole('md:SPSSODescriptor')
}
//TODO: enhance
mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter ->
if (filter instanceof EntityAttributesFilter) {
EntityAttributesFilter entityAttributesFilter = (EntityAttributesFilter) filter
MetadataFilter('xsi:type': 'EntityAttributes') {
// TODO: enhance. currently this does weird things with namespaces
entityAttributesFilter.attributes.each { attribute ->
mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(attribute, false))
}
if (entityAttributesFilter.entityAttributesFilterTarget.entityAttributesFilterTargetType == EntityAttributesFilterTarget.EntityAttributesFilterTargetType.ENTITY) {
entityAttributesFilter.entityAttributesFilterTarget.value.each {
Entity(it)
}
}
}
}
constructXmlNodeForFilter(filter, delegate)
}
}
}
Expand Down Expand Up @@ -177,11 +168,37 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
httpCacheDirectory: resolver.httpMetadataResolverAttributes?.httpCacheDirectory,
httpMaxCacheEntries: resolver.httpMetadataResolverAttributes?.httpMaxCacheEntries,
httpMaxCacheEntrySize: resolver.httpMetadataResolverAttributes?.httpMaxCacheEntrySize) {

childNodes()
}
}

void constructXmlNodeForFilter(EntityAttributesFilter filter, def markupBuilderDelegate) {
markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
// TODO: enhance. currently this does weird things with namespaces
filter.attributes.each { attribute ->
mkp.yieldUnescaped(openSamlObjects.marshalToXmlString(attribute, false))
}
if (filter.entityAttributesFilterTarget.entityAttributesFilterTargetType == EntityAttributesFilterTarget
.EntityAttributesFilterTargetType.ENTITY) {
filter.entityAttributesFilterTarget.value.each {
Entity(it)
}
}
}
}

void constructXmlNodeForFilter(EntityRoleWhiteListFilter filter, def markupBuilderDelegate) {
markupBuilderDelegate.MetadataFilter(
'xsi:type': 'EntityRoleWhiteList',
'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata'
) {
filter.retainedRoles.each {
markupBuilderDelegate.RetainedRole(it)
}
}
}

void constructXmlNodeForResolver(FileBackedHttpMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) {
markupBuilderDelegate.MetadataProvider(id: resolver.name,
'xsi:type': 'FileBackedHTTPMetadataProvider',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package edu.internet2.tier.shibboleth.admin.ui;

import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Profile;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

@SpringBootApplication
@EntityScan(basePackages = "edu.internet2.tier.shibboleth.admin.ui.domain")
Expand All @@ -23,4 +29,18 @@ public static void main(String... args) {
SpringApplication.run(ShibbolethUiApplication.class, args);
}

@Component
@Profile("dev")
public static class MetadataResolversResourceIdEmitter {

@Autowired
MetadataResolverRepository metadataResolverRepository;

@EventListener
void showMetadataResolversResourceIds(ApplicationStartedEvent e) {
metadataResolverRepository.findAll()
.forEach(it -> System.out.println(String.format("MetadataResolver [%s: %s]", it.getName(), it.getResourceId())));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,4 @@ protected void processConditionalRetrievalHeaders(HttpResponse response) {
metadataResolver.initialize();
return metadataResolver;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand All @@ -36,53 +37,69 @@ public class MetadataFiltersController {
@Autowired
private MetadataResolverRepository repository;

@Autowired
private FilterService filterService;

@Autowired
private MetadataResolverService metadataResolverService;

@GetMapping("/Filters")
@Transactional(readOnly = true)
public Iterable<MetadataFilter> getAll(@PathVariable String metadataResolverId) {
// TODO: implement lookup based on metadataResolverId once we have more than one
return repository.findAll().iterator().next().getMetadataFilters();
public ResponseEntity<?> getAll(@PathVariable String metadataResolverId) {
MetadataResolver resolver = repository.findByResourceId(metadataResolverId);
if(resolver == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(resolver.getMetadataFilters());
}

@GetMapping("/Filters/{resourceId}")
public ResponseEntity<?> getOne(@PathVariable String metadataResolverId, @PathVariable String resourceId) {
// TODO: implement lookup based on metadataResolverId once we have more than one
// TODO: should we check that we found exactly one filter (as in the update method below)? If not, error?
return ResponseEntity.ok(repository.findAll().iterator().next().getMetadataFilters().stream()
MetadataResolver resolver = repository.findByResourceId(metadataResolverId);
if(resolver == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(resolver.getMetadataFilters().stream()
.filter(f -> f.getResourceId().equals(resourceId))
.collect(Collectors.toList()).get(0));
}

@PostMapping("/Filters")
public ResponseEntity<?> create(@PathVariable String metadataResolverId, @RequestBody MetadataFilter createdFilter) {
//TODO: replace with get by metadataResolverId once we have more than one
MetadataResolver metadataResolver = repository.findAll().iterator().next();
MetadataResolver metadataResolver = repository.findByResourceId(metadataResolverId);
if(metadataResolver == null) {
return ResponseEntity.notFound().build();
}
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) {
//TODO: replace with get by metadataResolverId once we have more than one
MetadataResolver metadataResolver = repository.findAll().iterator().next();
public ResponseEntity<?> update(@PathVariable String metadataResolverId,
@PathVariable String resourceId,
@RequestBody MetadataFilter updatedFilter) {

MetadataResolver metadataResolver = repository.findByResourceId(metadataResolverId);
if(metadataResolver == null) {
return ResponseEntity.notFound().build();
}

if (!resourceId.equals(updatedFilter.getResourceId())) {
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}

List<MetadataFilter> filters =
metadataResolver.getMetadataFilters().stream()
Expand All @@ -105,14 +122,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<MetadataFilter> 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;
}

/**
Expand All @@ -126,7 +160,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import java.util.ArrayList;

import java.util.List;

import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromAttributeReleaseList;
Expand Down Expand Up @@ -64,6 +65,8 @@ public void fromTransientRepresentation() {
attributeList.addAll(getAttributeListFromAttributeReleaseList(this.attributeRelease));
attributeList.addAll(getAttributeListFromRelyingPartyOverridesRepresentation(this.relyingPartyOverrides));

this.attributes.addAll((List<edu.internet2.tier.shibboleth.admin.ui.domain.Attribute>)(List<? extends org.opensaml.saml.saml2.core.Attribute>)attributeList);
if(!attributeList.isEmpty()) {
this.attributes = (List<edu.internet2.tier.shibboleth.admin.ui.domain.Attribute>) (List<? extends org.opensaml.saml.saml2.core.Attribute>) attributeList;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
* Repository to manage {@link MetadataResolver} instances.
*/
public interface MetadataResolverRepository extends CrudRepository<MetadataResolver, Long> {

MetadataResolver findByName(String name);

MetadataResolver findByResourceId(String resourceId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ class MetadataFiltersControllerTests extends Specification {

controller = new MetadataFiltersController (
repository: metadataResolverRepository,
filterService: filterService,
metadataResolverService: new MetadataResolverService() {
@Override
void reloadFilters(String metadataResolverName) {
Expand All @@ -88,8 +87,7 @@ class MetadataFiltersControllerTests extends Specification {
def metadataResolver = new MetadataResolver()
List<MetadataFilter> expectedContent = testObjectGenerator.buildAllTypesOfFilterList()
metadataResolver.setMetadataFilters(expectedContent)
List<MetadataResolver> metadataResolverList = [metadataResolver]
1 * metadataResolverRepository.findAll() >> metadataResolverList
1 * metadataResolverRepository.findByResourceId(_) >> metadataResolver

def expectedHttpResponseStatus = status().isOk()
def expectedResponseContentType = APPLICATION_JSON_UTF8
Expand All @@ -109,7 +107,7 @@ class MetadataFiltersControllerTests extends Specification {
def metadataResolver = new MetadataResolver()
def expectedFilter = testObjectGenerator.entityAttributesFilter()
metadataResolver.metadataFilters = [expectedFilter]
1 * metadataResolverRepository.findAll() >> [metadataResolver]
1 * metadataResolverRepository.findByResourceId(_) >> metadataResolver

def expectedResourceId = expectedFilter.resourceId
def expectedHttpResponseStatus = status().isOk()
Expand All @@ -122,13 +120,10 @@ class MetadataFiltersControllerTests extends Specification {
result.andExpect(expectedHttpResponseStatus)
.andExpect(content().contentType(expectedResponseContentType))
.andExpect(content().json(mapper.writeValueAsString(expectedFilter)))
.andDo(MockMvcResultHandlers.print())
}

def "FilterController.create creates the desired filter"() {
given:
controller.filterService = mockFilterService // so we can control ids

def randomFilter = testObjectGenerator.entityAttributesFilter()
def metadataResolver = new MetadataResolver()
metadataResolver.setResourceId(randomGenerator.randomId())
Expand All @@ -138,7 +133,7 @@ class MetadataFiltersControllerTests extends Specification {
metadataResolverWithFilter.metadataFilters = metadataResolver.metadataFilters.collect()
metadataResolverWithFilter.getMetadataFilters().add(randomFilter)

1 * metadataResolverRepository.findAll() >> [metadataResolver]
1 * metadataResolverRepository.findByResourceId(_) >> metadataResolver
1 * metadataResolverRepository.save(_) >> metadataResolverWithFilter

def expectedMetadataResolverUUID = metadataResolver.getResourceId()
Expand Down Expand Up @@ -180,7 +175,7 @@ class MetadataFiltersControllerTests extends Specification {
updatedMetadataResolver.setMetadataFilters(originalMetadataResolver.getMetadataFilters().collect())
updatedMetadataResolver.getMetadataFilters().add(updatedFilter)

1 * metadataResolverRepository.findAll() >> [originalMetadataResolver]
1 * metadataResolverRepository.findByResourceId(_) >> originalMetadataResolver
1 * metadataResolverRepository.save(_) >> updatedMetadataResolver

def filterUUID = updatedFilter.getResourceId()
Expand All @@ -193,6 +188,7 @@ class MetadataFiltersControllerTests extends Specification {

then:
def expectedJson = new JsonSlurper().parseText(postedJsonBody)
updatedFilter.fromTransientRepresentation()
expectedJson << [version: updatedFilter.hashCode()]
result.andExpect(status().isOk())
.andExpect(content().json(JsonOutput.toJson(expectedJson), true))
Expand All @@ -210,7 +206,7 @@ class MetadataFiltersControllerTests extends Specification {
originalMetadataResolver.setMetadataFilters(testObjectGenerator.buildAllTypesOfFilterList())
originalMetadataResolver.getMetadataFilters().add(randomFilter)

1 * metadataResolverRepository.findAll() >> [originalMetadataResolver]
1 * metadataResolverRepository.findByResourceId(_) >> originalMetadataResolver

def filterUUID = randomFilter.getResourceId()

Expand Down
Loading

0 comments on commit 22c7d0e

Please sign in to comment.