Skip to content

Commit

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

Approved-by: Shibui Jenkins <shibui.jenkins@gmail.com>
  • Loading branch information
dima767 authored and Jonathan Johnson committed Jul 3, 2018
2 parents 42a10b2 + fdf5da0 commit 2d724c4
Show file tree
Hide file tree
Showing 42 changed files with 1,409 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.*
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository
import groovy.util.logging.Slf4j
Expand All @@ -22,6 +23,9 @@ import org.opensaml.saml.saml2.metadata.EntityDescriptor
import org.springframework.beans.factory.annotation.Autowired
import org.w3c.dom.Document

import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver.ResourceType.CLASSPATH
import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver.ResourceType.SVN

@Slf4j
class JPAMetadataResolverServiceImpl implements MetadataResolverService {

Expand Down Expand Up @@ -272,4 +276,49 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
childNodes()
}
}

void constructXmlNodeForResolver(ResourceBackedMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) {
//This might throw an InvalidResourceTypeException if both resource types do not satisfy validation rules
//But this validation step already would have been performed by higher app layers such as REST controllers,
//and if this is not done, an exception thrown here would be trully considered a server side error bug which would
//need to be taken care of
def resourceType = resolver.validateAndDetermineResourceType()

markupBuilderDelegate.MetadataProvider(
id: resolver.name,
'xsi:type': 'ResourceBackedMetadataProvider',
parserPoolRef: resolver.reloadableMetadataResolverAttributes?.parserPoolRef,
minRefreshDelay: resolver.reloadableMetadataResolverAttributes?.minRefreshDelay,
maxRefreshDelay: resolver.reloadableMetadataResolverAttributes?.maxRefreshDelay,
refreshDelayFactor: resolver.reloadableMetadataResolverAttributes?.refreshDelayFactor,
indexesRef: resolver.reloadableMetadataResolverAttributes?.indexesRef,
resolveViaPredicatesOnly: resolver.reloadableMetadataResolverAttributes?.resolveViaPredicatesOnly ?: null,
expirationWarningThreshold: resolver.reloadableMetadataResolverAttributes?.expirationWarningThreshold) {

if(resourceType == SVN) {
MetadataResource(
'xmlns:resource': 'urn:mace:shibboleth:2.0:resource',
'xsi:type': 'resource:SVNResource',
'resourceFile': resolver.svnMetadataResource.resourceFile,
'repositoryURL': resolver.svnMetadataResource.repositoryURL,
'workingCopyDirectory': resolver.svnMetadataResource.workingCopyDirectory,
'username': resolver.svnMetadataResource.username,
'password': resolver.svnMetadataResource.password,
'proxyHost': resolver.svnMetadataResource.proxyHost,
'proxyPort': resolver.svnMetadataResource.proxyHost,
'proxyUserName': resolver.svnMetadataResource.proxyUserName,
'proxyPassword': resolver.svnMetadataResource.proxyPassword)

}
else if (resourceType == CLASSPATH) {
MetadataResource(
'xmlns:resource': 'urn:mace:shibboleth:2.0:resource',
'xsi:type': 'resource:ClasspathResource',
'file': resolver.classpathMetadataResource.file)
}

childNodes()
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolverValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class MetadataResolverValidationConfiguration {

@Bean
ResourceBackedMetadataResolverValidator resourceBackedMetadataResolverValidator() {
return new ResourceBackedMetadataResolverValidator();
}

@Bean
@SuppressWarnings("Unchecked")
MetadataResolverValidationService metadataResolverValidationService(List<MetadataResolverValidator> metadataResolverValidators) {
return new MetadataResolverValidationService(metadataResolverValidators);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import java.util.stream.Stream;

@RestController
@RequestMapping("/api/MetadataResolver/{metadataResolverId}")
@RequestMapping("/api/MetadataResolvers/{metadataResolverId}")
public class MetadataFiltersController {

private static Logger LOGGER = LoggerFactory.getLogger(MetadataFiltersController.class);
Expand Down Expand Up @@ -194,7 +194,7 @@ else if(filterWithUpdatedData instanceof RequiredValidUntilFilter) {

private static URI getResourceUriFor(MetadataResolver mr, String filterResourceId) {
return ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/api/MetadataResolver/")
.fromCurrentServletMapping().path("/api/MetadataResolvers/")
.pathSegment(mr.getResourceId())
.pathSegment("Filters")
.pathSegment(filterResourceId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;

import lombok.extern.slf4j.Slf4j;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;

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

@RestController
@RequestMapping("/api")
@Slf4j
public class MetadataResolversController {

@Autowired
MetadataResolverRepository resolverRepository;

@Autowired
MetadataResolverValidationService metadataResolverValidationService;

@GetMapping("/MetadataResolvers")
@Transactional(readOnly = true)
public ResponseEntity<?> getAll() {
Iterable<MetadataResolver> resolvers = resolverRepository.findAll();
resolvers.forEach(MetadataResolver::updateVersion);
return ResponseEntity.ok(resolvers);
}

@GetMapping("/MetadataResolvers/{resourceId}")
@Transactional(readOnly = true)
public ResponseEntity<?> getOne(@PathVariable String resourceId) {
MetadataResolver resolver = resolverRepository.findByResourceId(resourceId);
if (resolver == null) {
return ResponseEntity.notFound().build();
}
resolver.updateVersion();
return ResponseEntity.ok(resolver);
}

@PostMapping("/MetadataResolvers")
@Transactional
public ResponseEntity<?> create(@RequestBody MetadataResolver newResolver) {
//TODO: we are disregarding attached filters if any sent from UI.
//Only deal with filters via filters endpoints?
newResolver.clearAllFilters();

ResponseEntity<?> validationErrorResponse = validate(newResolver);
if(validationErrorResponse != null) {
return validationErrorResponse;
}

MetadataResolver persistedResolver = resolverRepository.save(newResolver);
persistedResolver.updateVersion();

return ResponseEntity.created(getResourceUriFor(persistedResolver)).body(persistedResolver);
}

@PutMapping("/MetadataResolvers/{resourceId}")
@Transactional
public ResponseEntity<?> update(@PathVariable String resourceId, @RequestBody MetadataResolver updatedResolver) {
MetadataResolver existingResolver = resolverRepository.findByResourceId(resourceId);
if (existingResolver == null) {
return ResponseEntity.notFound().build();
}
if (existingResolver.hashCode() != updatedResolver.getVersion()) {
log.info("Metadata Resolver version conflict. Latest resolver in database version: {}. Resolver version sent from UI: {}",
existingResolver.hashCode(), updatedResolver.getVersion());
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}

ResponseEntity<?> validationErrorResponse = validate(updatedResolver);
if(validationErrorResponse != null) {
return validationErrorResponse;
}

updatedResolver.setAudId(existingResolver.getAudId());

//TODO: we are disregarding attached filters if any sent from UI.
//Only deal with filters via filters endpoints?
updatedResolver.setMetadataFilters(existingResolver.getMetadataFilters());

MetadataResolver persistedResolver = resolverRepository.save(updatedResolver);
persistedResolver.updateVersion();

return ResponseEntity.ok(persistedResolver);
}

@SuppressWarnings("Unchecked")
private ResponseEntity<?> validate(MetadataResolver metadataResolver) {
ValidationResult validationResult = metadataResolverValidationService.validateIfNecessary(metadataResolver);
if(!validationResult.isValid()) {
return ResponseEntity.badRequest().body(validationResult.getErrorMessage());
}
return null;
}

private static URI getResourceUriFor(MetadataResolver resolver) {
return ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/api/MetadataResolvers/")
.pathSegment(resolver.getResourceId())
.build()
.toUri();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public EntityAttributesFilter() {
public void intoTransientRepresentation() {
this.attributeRelease = getAttributeReleaseListFromAttributeList(this.attributes);
this.relyingPartyOverrides = getRelyingPartyOverridesRepresentationFromAttributeList(attributes);
setVersion(hashCode());
updateVersion();
}

@PrePersist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ public class MetadataFilter extends AbstractAuditable {

@Transient
private int version;

public void updateVersion() {
this.version = hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.Embeddable;

@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class ClasspathMetadataResource {

private String file;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
@ToString
public class DynamicHttpMetadataResolver extends MetadataResolver {



public static final String DEFAULT_TIMEOUT = "PT5S";

@Embedded
Expand All @@ -38,6 +40,7 @@ public class DynamicHttpMetadataResolver extends MetadataResolver {
private List<String> supportedContentTypes;

public DynamicHttpMetadataResolver() {
type = "DynamicHttpMetadataResolver";
this.httpMetadataResolverAttributes = new HttpMetadataResolverAttributes();
this.httpMetadataResolverAttributes.setConnectionRequestTimeout(DEFAULT_TIMEOUT);
this.httpMetadataResolverAttributes.setConnectionTimeout(DEFAULT_TIMEOUT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@

@Entity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Getter
@Setter
@ToString
public class FileBackedHttpMetadataResolver extends MetadataResolver {

public FileBackedHttpMetadataResolver() {
type = "FileBackedHttpMetadataResolver";
}

private String metadataURL;

private String backingFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

Expand All @@ -11,12 +10,15 @@

@Entity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Getter
@Setter
@ToString
public class LocalDynamicMetadataResolver extends MetadataResolver {

public LocalDynamicMetadataResolver() {
type = "LocalDynamicMetadataResolver";
}

private String sourceDirectory;

private String sourceManagerRef;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import lombok.EqualsAndHashCode;
Expand All @@ -22,17 +25,25 @@

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@EqualsAndHashCode(callSuper = true, exclude={"version"})
@EqualsAndHashCode(callSuper = true, exclude = {"version"})
@NoArgsConstructor
@Getter
@Setter
@ToString
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@type", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(value = LocalDynamicMetadataResolver.class, name = "LocalDynamicMetadataResolver"),
@JsonSubTypes.Type(value = FileBackedHttpMetadataResolver.class, name = "FileBackedHttpMetadataResolver"),
@JsonSubTypes.Type(value = DynamicHttpMetadataResolver.class, name = "DynamicHttpMetadataResolver")})
public class MetadataResolver extends AbstractAuditable {

@Column(unique=true)
@JsonProperty("@type")
@Transient
String type = "BaseMetadataResolver";

@Column(unique = true)
private String name;

@Column(unique=true)
@Column(unique = true)
private String resourceId = UUID.randomUUID().toString();

private Boolean requireValidMetadata = true;
Expand All @@ -53,4 +64,12 @@ public class MetadataResolver extends AbstractAuditable {

@Transient
private int version;

public void updateVersion() {
this.version = hashCode();
}

public void clearAllFilters() {
this.metadataFilters.clear();
}
}
Loading

0 comments on commit 2d724c4

Please sign in to comment.