Skip to content

Commit

Permalink
Merged in SHIBUI-645 (pull request #130)
Browse files Browse the repository at this point in the history
SHIBUI-645

Approved-by: Shibui Jenkins <shibui.jenkins@gmail.com>
  • Loading branch information
dima767 authored and Jonathan Johnson committed Aug 8, 2018
2 parents 29d1576 + 63d6e5e commit 1da1d75
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
@Autowired
private OpenSamlObjects openSamlObjects

@Autowired
private MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService

// TODO: enhance
@Override
void reloadFilters(String metadataResolverName) {
Expand Down Expand Up @@ -98,8 +101,11 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
'xsi:type': 'ChainingMetadataProvider',
'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd'
) {
metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr ->
//TODO: We cannot/do not currently have the code to marshall the internal incommon chaining resolver


resolversPositionOrderContainerService.allMetadataResolversInDefinedOrderOrUnordered.each {
edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr ->
//TODO: We do not currently marshall the internal incommon chaining resolver (with BaseMetadataResolver type)
if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) {
constructXmlNodeForResolver(mr, delegate) {
MetadataFilter(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityIdsSearchResultRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer;
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.repository.MetadataResolverRepository;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.service.*;
import edu.internet2.tier.shibboleth.admin.util.AttributeUtility;
Expand Down Expand Up @@ -162,4 +165,14 @@ public void addInterceptors(InterceptorRegistry registry) {
}
};
}

@Bean
public MetadataResolversPositionOrderContainerService
metadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository
positionOrderContainerRepository,
MetadataResolverRepository resolverRepository) {

return new DefaultMetadataResolversPositionOrderContainerService(positionOrderContainerRepository, resolverRepository);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
Expand All @@ -30,6 +31,7 @@
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.List;

import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator.ValidationResult;

Expand All @@ -47,6 +49,9 @@ public class MetadataResolversController {
@Autowired
MetadataResolverService metadataResolverService;

@Autowired
MetadataResolversPositionOrderContainerService positionOrderContainerService;

@ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class})
public ResponseEntity<?> unableToParseJson(Exception ex) {
return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage()));
Expand All @@ -55,7 +60,7 @@ public ResponseEntity<?> unableToParseJson(Exception ex) {
@GetMapping("/MetadataResolvers")
@Transactional(readOnly = true)
public ResponseEntity<?> getAll() {
Iterable<MetadataResolver> resolvers = resolverRepository.findAll();
List<MetadataResolver> resolvers = positionOrderContainerService.getAllMetadataResolversInDefinedOrderOrUnordered();
resolvers.forEach(MetadataResolver::updateVersion);
return ResponseEntity.ok(resolvers);
}
Expand Down Expand Up @@ -99,6 +104,7 @@ public ResponseEntity<?> create(@RequestBody MetadataResolver newResolver) {

newResolver.convertFiltersFromTransientRepresentationIfNecessary();
MetadataResolver persistedResolver = resolverRepository.save(newResolver);
positionOrderContainerService.appendPositionOrderForNew(persistedResolver);
persistedResolver.updateVersion();

persistedResolver.convertFiltersIntoTransientRepresentationIfNecessary();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;


import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author Dmitriy Kopylenko
*/
@RestController
@RequestMapping("/api/MetadataResolversPositionOrder")
public class MetadataResolversPositionOrderController {

@Autowired
MetadataResolversPositionOrderContainerService positionOrderContainerService;

@PostMapping
public ResponseEntity<?> createOrUpdate(@RequestBody MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer) {
positionOrderContainerService.addOrUpdatePositionOrderContainer(metadataResolversPositionOrderContainer);
return ResponseEntity.noContent().build();
}

@GetMapping
public ResponseEntity<?> getPositionOrderContainer() {
return ResponseEntity.ok(positionOrderContainerService.retrieveExistingOrEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers;

/**
* This is a persistent entity abstraction encapsulating a collection of metadata resolver ids
* for the purpose of maintaining an order of all persistent metadata resolvers which becomes significant during
* generation of XML metadata for the resolvers.
*
* Maintaining this separate entity enables UI layer for example to explicitly manipulate ordering e.g. use REST
* API to reorder resolvers, etc.
*
* @author Dmitriy
*/
public class MetadataResolversOrderContainer {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OrderColumn;
import java.util.ArrayList;
import java.util.List;

/**
* This is a persistent entity abstraction encapsulating a collection of metadata resolver ids
* for the purpose of maintaining an order of all persistent metadata resolvers which becomes significant during
* generation of XML metadata for the resolvers.
*
* Maintaining this separate entity enables UI layer for example to explicitly manipulate ordering e.g. use REST
* API to reorder resolvers, etc.
*
* @author Dmitriy Kopylenko
*/
@Entity
@EqualsAndHashCode
@NoArgsConstructor
@Getter
@Setter
@ToString
public class MetadataResolversPositionOrderContainer {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@JsonIgnore
protected Long id;

@ElementCollection
@CollectionTable(name="METADATA_RESOLVER_POSITION_ORDER", joinColumns=@JoinColumn(name="METADATA_RESOLVER_POSITION_ORDER_CONTAINER_ID"))
@Column(name="METADATA_RESOLVER_RESOURCE_ID")
@OrderColumn
private List<String> resourceIds = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer;
import org.springframework.data.repository.CrudRepository;

/**
* Spring Data Repository API for persistence operations on instances of {@link MetadataResolversPositionOrderContainer}.
*
* @author Dmitriy Kopylenko
*/
public interface MetadataResolversPositionOrderContainerRepository
extends CrudRepository<MetadataResolversPositionOrderContainer, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import static com.google.common.collect.FluentIterable.from;
import static java.util.stream.Collectors.toList;


/**
* Default implementation of {@link MetadataResolversPositionOrderContainer}.
*
* @author Dmitriy Kopylenko
*/
public class DefaultMetadataResolversPositionOrderContainerService implements MetadataResolversPositionOrderContainerService {

private MetadataResolversPositionOrderContainerRepository positionOrderContainerRepository;

private MetadataResolverRepository metadataResolverRepository;

public DefaultMetadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository positionOrderRepository,
MetadataResolverRepository metadataResolverRepository) {
this.positionOrderContainerRepository = positionOrderRepository;
this.metadataResolverRepository = metadataResolverRepository;
}

@Override
@Transactional
public void addOrUpdatePositionOrderContainer(MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer) {
MetadataResolversPositionOrderContainer existingPositionOrder = getPositionOrderContainerIfExists().orElse(null);
if (existingPositionOrder != null) {
existingPositionOrder.setResourceIds(metadataResolversPositionOrderContainer.getResourceIds());
positionOrderContainerRepository.save(existingPositionOrder);
return;
}
positionOrderContainerRepository.save(metadataResolversPositionOrderContainer);
}

@Override
@Transactional(readOnly = true)
public List<MetadataResolver> getAllMetadataResolversInDefinedOrderOrUnordered() {
Optional<MetadataResolversPositionOrderContainer> orderContainer = getPositionOrderContainerIfExists();
if(orderContainer.isPresent()) {
return orderContainer.get().getResourceIds()
.stream()
.map(metadataResolverRepository::findByResourceId)
.collect(toList());
}

return from(metadataResolverRepository.findAll()).toList();
}

@Override
public MetadataResolversPositionOrderContainer retrieveExistingOrEmpty() {
return getPositionOrderContainerIfExists().orElseGet(MetadataResolversPositionOrderContainer::new);
}

@Override
@Transactional
public void appendPositionOrderForNew(MetadataResolver metadataResolver) {
MetadataResolversPositionOrderContainer positionOrderContainer = retrieveExistingOrEmpty();
positionOrderContainer.getResourceIds().add(metadataResolver.getResourceId());
positionOrderContainerRepository.save(positionOrderContainer);
}

private Optional<MetadataResolversPositionOrderContainer> getPositionOrderContainerIfExists() {
Iterator<MetadataResolversPositionOrderContainer> iter = positionOrderContainerRepository.findAll().iterator();
return iter.hasNext() ? Optional.of(iter.next()) : Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer;

import java.util.List;

/**
* Service interface for manipulation of instances of {@link MetadataResolversPositionOrderContainer} and
* to abstract away MetadataResolvers ordering logic.
*
* @author Dmitriy Kopylenko
*/
public interface MetadataResolversPositionOrderContainerService {

MetadataResolversPositionOrderContainer retrieveExistingOrEmpty();

void addOrUpdatePositionOrderContainer(MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer);

List<MetadataResolver> getAllMetadataResolversInDefinedOrderOrUnordered();

void appendPositionOrderForNew(MetadataResolver metadataResolver);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package edu.internet2.tier.shibboleth.admin.ui.service

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository
import spock.lang.Specification
import spock.lang.Subject

/**
* @author Dmitriy Kopylenko
*/
class MetadataResolversPositionOrderContainerServiceTests extends Specification {

def "no order container has been provided and saved, so using unordered persisted resolvers"() {
given:
def resolverRepo = Mock(MetadataResolverRepository)
resolverRepo.findAll() >> [new MetadataResolver(resourceId: 'second'), new MetadataResolver(resourceId: 'first')]
def positionOrderRepo = Mock(MetadataResolversPositionOrderContainerRepository)
positionOrderRepo.findAll() >> []
@Subject
def positionContainerSvc = new DefaultMetadataResolversPositionOrderContainerService(positionOrderRepo, resolverRepo)

when:
def unorderedResolvers = positionContainerSvc.getAllMetadataResolversInDefinedOrderOrUnordered()

then:
unorderedResolvers[0].resourceId == 'second'
unorderedResolvers[1].resourceId == 'first'

}

def "an order container has been provided and saved, so using resolvers with order defined in that position order container"() {
given:
def resolverRepo = Mock(MetadataResolverRepository)
resolverRepo.findAll() >> [new MetadataResolver(resourceId: 'second'), new MetadataResolver(resourceId: 'first')]
resolverRepo.findByResourceId('first') >> new MetadataResolver(resourceId: 'first')
resolverRepo.findByResourceId('second') >> new MetadataResolver(resourceId: 'second')
def positionOrderRepo = Mock(MetadataResolversPositionOrderContainerRepository)
positionOrderRepo.findAll() >> [new MetadataResolversPositionOrderContainer(resourceIds: ['first', 'second'])]
positionOrderRepo.findAll() >> []
@Subject
def positionContainerSvc = new DefaultMetadataResolversPositionOrderContainerService(positionOrderRepo, resolverRepo)

when:
def orderedResolvers = positionContainerSvc.getAllMetadataResolversInDefinedOrderOrUnordered()

then:
orderedResolvers[0].resourceId == 'first'
orderedResolvers[1].resourceId == 'second'

}
}

0 comments on commit 1da1d75

Please sign in to comment.