Skip to content

Commit

Permalink
Fixed merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed Dec 11, 2018
2 parents 2d97630 + e56efaa commit f41025e
Show file tree
Hide file tree
Showing 32 changed files with 340 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.service

import com.google.common.base.Predicate
import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration
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
Expand Down Expand Up @@ -52,6 +53,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
@Autowired
private MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService

@Autowired
private ShibUIConfiguration shibUIConfiguration

// TODO: enhance
@Override
void reloadFilters(String metadataResolverResourceId) {
Expand All @@ -64,6 +68,13 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {

List<MetadataFilter> metadataFilters = new ArrayList<>()

// set up namespace protection
if (shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0) {
def target = new org.opensaml.saml.metadata.resolver.filter.impl.EntityAttributesFilter()
target.attributeFilter = new ScriptedPredicate(new EvaluableScript(protectedNamespaceScript()))
metadataFilters.add(target)
}

for (edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter metadataFilter : jpaMetadataResolver.getMetadataFilters()) {
if (metadataFilter instanceof EntityAttributesFilter) {
EntityAttributesFilter entityAttributesFilter = (EntityAttributesFilter) metadataFilter
Expand Down Expand Up @@ -104,6 +115,21 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}

private String protectedNamespaceScript() {
return """(function (attribute) {
"use strict";
var namespaces = [${shibUIConfiguration.protectedAttributeNamespaces.collect({"\"${it}\""}).join(', ')}];
// check the parameter
if (attribute === null) { return true; }
for (var i in namespaces) {
if (attribute.getName().startsWith(namespaces[i])) {
return false;
}
}
return true;
}(input));"""
}

private class ScriptedPredicate extends net.shibboleth.utilities.java.support.logic.ScriptedPredicate<EntityDescriptor> {
protected ScriptedPredicate(@Nonnull EvaluableScript theScript) {
super(theScript)
Expand Down Expand Up @@ -133,9 +159,18 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) {
constructXmlNodeForResolver(mr, delegate) {
//TODO: enhance
def didNamespaceProtectionFilter = !(shibUIConfiguration.protectedAttributeNamespaces && shibUIConfiguration.protectedAttributeNamespaces.size() > 0)
mr.metadataFilters.each { edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter filter ->
if (filter instanceof EntityAttributesFilter && !didNamespaceProtectionFilter) {
constructXmlNodeForEntityAttributeNamespaceProtection(delegate)
didNamespaceProtectionFilter = true
}
constructXmlNodeForFilter(filter, delegate)
}
if (!didNamespaceProtectionFilter) {
constructXmlNodeForEntityAttributeNamespaceProtection(delegate)
didNamespaceProtectionFilter = true
}
}
}
}
Expand All @@ -144,6 +179,16 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}
}

void constructXmlNodeForEntityAttributeNamespaceProtection(def markupBuilderDelegate) {
markupBuilderDelegate.MetadataFilter('xsi:type': 'EntityAttributes') {
AttributeFilterScript() {
Script() {
mkp.yieldUnescaped("\n<![CDATA[\n${protectedNamespaceScript()}\n]]>\n")
}
}
}
}

void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) {
if(filter.xmlShouldBeGenerated()) {
markupBuilderDelegate.MetadataFilter(id: filter.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import javax.servlet.http.HttpServletRequest;

@Configuration
@EnableConfigurationProperties(CustomPropertiesConfiguration.class)
@EnableConfigurationProperties({CustomPropertiesConfiguration.class, ShibUIConfiguration.class})
public class CoreShibUiConfiguration {
private static final Logger logger = LoggerFactory.getLogger(CoreShibUiConfiguration.class);

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

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@ConfigurationProperties(prefix = "shibui")
@Getter
@Setter
public class ShibUIConfiguration {
/**
* A list of namespaces that should be excluded from incoming metadata. This is used to prevent third party metadata
* sources from using attributes that they might not have the rights to use.
*/
private List<String> protectedAttributeNamespaces;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException;
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.opensaml.OpenSamlChainingMetadataResolver;
Expand Down Expand Up @@ -105,32 +106,26 @@ public ResponseEntity<?> getOne(@PathVariable String resourceId) {
}

@PostMapping("/MetadataResolvers")
@Transactional
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> create(@RequestBody MetadataResolver newResolver) throws IOException, ResolverException, ComponentInitializationException {
if (resolverRepository.findByName(newResolver.getName()) != null) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}

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

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

//TODO: currently, the update call might explode, but the save works.. in which case, the UI never gets
// n valid response. This operation is not atomic. Should we return an error here?
if (persistedResolver.getDoInitialization()) {
org.opensaml.saml.metadata.resolver.MetadataResolver openSamlRepresentation = metadataResolverConverterService.convertToOpenSamlRepresentation(persistedResolver);
OpenSamlChainingMetadataResolverUtil.updateChainingMetadataResolver((OpenSamlChainingMetadataResolver) chainingMetadataResolver, openSamlRepresentation);
}
doResolverInitialization(persistedResolver);

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

@PutMapping("/MetadataResolvers/{resourceId}")
@Transactional
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> update(@PathVariable String resourceId, @RequestBody MetadataResolver updatedResolver) throws IOException, ResolverException, ComponentInitializationException {
MetadataResolver existingResolver = resolverRepository.findByResourceId(resourceId);
if (existingResolver == null) {
Expand All @@ -143,32 +138,21 @@ public ResponseEntity<?> update(@PathVariable String resourceId, @RequestBody Me
}

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

updatedResolver.setAudId(existingResolver.getAudId());

MetadataResolver persistedResolver = resolverRepository.save(updatedResolver);

if (persistedResolver.getDoInitialization()) {
org.opensaml.saml.metadata.resolver.MetadataResolver openSamlRepresentation = null;
try {
openSamlRepresentation = metadataResolverConverterService.convertToOpenSamlRepresentation(persistedResolver);
} catch (FileNotFoundException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.toString(), "label.file-doesnt-exist"));
}
OpenSamlChainingMetadataResolverUtil.updateChainingMetadataResolver((OpenSamlChainingMetadataResolver) chainingMetadataResolver, openSamlRepresentation);
}
doResolverInitialization(persistedResolver);

return ResponseEntity.ok(persistedResolver);
}

@SuppressWarnings("Unchecked")
private ResponseEntity<?> validate(MetadataResolver metadataResolver) {
ValidationResult validationResult = metadataResolverValidationService.validateIfNecessary(metadataResolver);
if(!validationResult.isValid()) {
if (!validationResult.isValid()) {
return ResponseEntity.badRequest().body(validationResult.getErrorMessage());
}
return null;
Expand All @@ -181,4 +165,17 @@ private static URI getResourceUriFor(MetadataResolver resolver) {
.build()
.toUri();
}

private void doResolverInitialization(MetadataResolver persistedResolver) throws
ComponentInitializationException, ResolverException, IOException {
if (persistedResolver.getDoInitialization()) {
org.opensaml.saml.metadata.resolver.MetadataResolver openSamlRepresentation = null;
try {
openSamlRepresentation = metadataResolverConverterService.convertToOpenSamlRepresentation(persistedResolver);
} catch (FileNotFoundException e) {
throw new MetadataFileNotFoundException("message.file-doesnt-exist");
}
OpenSamlChainingMetadataResolverUtil.updateChainingMetadataResolver((OpenSamlChainingMetadataResolver) chainingMetadataResolver, openSamlRepresentation);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.controller.support;

import com.google.common.collect.ImmutableMap;
import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse;
import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import org.hibernate.exception.ConstraintViolationException;
Expand All @@ -12,6 +12,7 @@
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;

import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.NOT_FOUND;

Expand Down Expand Up @@ -53,4 +54,10 @@ public final ResponseEntity<ErrorResponse> handleAllOtherExceptions(Exception ex
ErrorResponse errorResponse = new ErrorResponse("400", ex.getLocalizedMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(MetadataFileNotFoundException.class)
public final ResponseEntity<ErrorResponse> metadataFileNotFoundHandler(MetadataFileNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(INTERNAL_SERVER_ERROR.toString(), ex.getLocalizedMessage());
return new ResponseEntity<>(errorResponse, INTERNAL_SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.exceptions;

/**
* Indicates that provided metadata file is not found. Thrown during opensaml API initialization code path, if there is
* such. Throwing such exception is useful in @Transactional context for atomic transaction rollbacks, etc.
*
* @author Dmitriy Kopylenko
*/
public class MetadataFileNotFoundException extends RuntimeException {

public MetadataFileNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private OpenSamlFilesystemMetadataResolver convertToOpenSamlRepresentation(Files
IndexWriter indexWriter = indexWriterService.getIndexWriter(resolver.getResourceId());
File metadataFile = new File(resolver.getMetadataFile());
if (resolver.getDoInitialization() && !metadataFile.exists()) {
throw new FileNotFoundException("No file was found on the fileysystem for provided filename: " + resolver.getMetadataFile());
throw new FileNotFoundException("No file was found on the file system for provided filename: " + resolver.getMetadataFile());
}

OpenSamlFilesystemMetadataResolver openSamlResolver = new OpenSamlFilesystemMetadataResolver(openSamlObjects.getParserPool(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@
"step": 0.01
},
"placeholder": "label.real-number",
"minimum": 0,
"maximum": 1,
"minimum": 0.01,
"maximum": 0.99,
"default": null
},
"minCacheDuration": {
Expand Down Expand Up @@ -222,6 +222,29 @@
"default": null,
"pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$"
},
"removeIdleEntityData": {
"title": "label.remove-idle-entity-data",
"description": "tooltip.remove-idle-entity-data",
"type": "boolean",
"widget": {
"id": "boolean-radio"
},
"oneOf": [
{
"enum": [
true
],
"description": "value.true"
},
{
"enum": [
false
],
"description": "value.false"
}
],
"default": true
},
"cleanupTaskInterval": {
"title": "label.cleanup-task-interval",
"description": "tooltip.cleanup-task-interval",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"properties": {
"name": {
"title": "label.metadata-provider-name",
"title": "label.service-provider-name-dashboard-display-only",
"description": "tooltip.metadata-provider-name",
"type": "string",
"widget": {
Expand Down Expand Up @@ -135,7 +135,7 @@
},
"placeholder": "label.real-number",
"minimum": 0,
"maximum": 1,
"maximum": 0.99,
"default": null
}
}
Expand Down
3 changes: 2 additions & 1 deletion backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ label.metadata-provider-type=Metadata Provider Type
label.metadata-provider-name=Metadata Provider Name
label.select-metadata-type=Select a metadata provider type
label.metadata-provider-status=Metadata Provider Status
label.enable-provider-upon-saving=Enable Metadata Provider?
label.enable-provider-upon-saving=If checkbox is clicked, the metadata provider is enabled for integration with the IdP
label.certificate-type=Type

label.metadata-file=Metadata File
Expand Down Expand Up @@ -398,6 +398,7 @@ message.wizard-status=Step { index } of { length }
message.entity-id-min-unique=You must add at least one entity id target and they must each be unique.
message.required-for-scripts=Required for Scripts
message.required-for-regex=Required for Regex
message.file-doesnt-exist=The requested file to be processed does not exist on the server.
message.database-constraint=There was a database constraint problem processing the request. Check the request to ensure that fields that must be unique are truly unique.

tooltip.entity-id=Entity ID
Expand Down
9 changes: 5 additions & 4 deletions backend/src/main/resources/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ label.metadata-provider-type=Metadata Provider Type
label.metadata-provider-name=Metadata Provider Name
label.select-metadata-type=Select a metadata provider type
label.metadata-provider-status=Metadata Provider Status
label.enable-provider-upon-saving=Enable Metadata Provider?
label.enable-provider-upon-saving=If checkbox is clicked, the metadata provider is enabled for integration with the IdP
label.certificate-type=Type

label.metadata-file=Metadata File
Expand Down Expand Up @@ -404,6 +404,7 @@ message.wizard-status=Step { index } of { length }
message.entity-id-min-unique=You must add at least one entity id target and they must each be unique.
message.required-for-scripts=Required for Scripts
message.required-for-regex=Required for Regex
message.file-doesnt-exist=The requested file to be processed does not exist on the server.
message.database-constraint=There was a database constraint problem processing the request. Check the request to ensure that fields that must be unique are truly unique.

tooltip.entity-id=Entity ID
Expand Down Expand Up @@ -482,9 +483,9 @@ tooltip.enable-provider-upon-saving=If checkbox is clicked, the metadata provide
tooltip.max-validity-interval=Defines the window within which the metadata is valid.
tooltip.require-signed-root=If true, this fails to load metadata with no signature on the root XML element.
tooltip.certificate-file=A key used to verify the signature. Conflicts with trustEngineRef and both of the child elements.
tooltip.retained-roles=Controls whether to keep entity descriptors that contain no roles
tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles.
tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors.
tooltip.retained-roles=Note that property replacement cannot be used on this element.
tooltip.remove-roleless-entity-descriptors=Controls whether to keep entity descriptors that contain no roles. Note: If this attribute is set to false, the resulting output may not be schema-valid since an <md:EntityDescriptor> element must include at least one role descriptor.
tooltip.remove-empty-entities-descriptors=Controls whether to keep entities descriptors that contain no entity descriptors. Note: If this attribute is set to false, the resulting output may not be schema-valid since an <md:EntitiesDescriptor> element must include at least one child element, either an <md:EntityDescriptor> element or an <md:EntitiesDescriptor> element.

tooltip.min-refresh-delay=Lower bound on the next refresh from the time calculated based on the metadata\u0027s expiration.
tooltip.max-refresh-delay=Upper bound on the next refresh from the time calculated based on the metadata\u0027s expiration.
Expand Down
Loading

0 comments on commit f41025e

Please sign in to comment.