Skip to content

Commit

Permalink
Merged in SHIBUI-799 (pull request #256)
Browse files Browse the repository at this point in the history
SHIBUI-799 NameID Format Filter
  • Loading branch information
rmathis authored and Jonathan Johnson committed Dec 11, 2018
2 parents e56efaa + 24f4d9a commit 0260d59
Show file tree
Hide file tree
Showing 55 changed files with 5,008 additions and 174 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package edu.internet2.tier.shibboleth.admin.ui.controller

import com.fasterxml.jackson.databind.ObjectMapper
import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation
import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry
import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService
import groovy.util.logging.Slf4j
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.RequestMapping
import org.springframework.web.bind.annotation.RestController

import javax.annotation.PostConstruct

import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup.nameIdFormatFilterSchema
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR

/**
* Controller implementing REST resource responsible for exposing structure definition for nameid format filter user
* interface in terms of JSON schema.
*
* @author Dmitriy Kopylenko
*/
@RestController
@RequestMapping('/api/ui/NameIdFormatFilter')
@Slf4j
class NameIdFormatFilterUiDefinitionController {

@Autowired
JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry

JsonSchemaResourceLocation jsonSchemaLocation

@Autowired
ObjectMapper jacksonObjectMapper

@Autowired
JsonSchemaBuilderService jsonSchemaBuilderService

@GetMapping
ResponseEntity<?> getUiDefinitionJsonSchema() {
try {
def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map)
jsonSchemaBuilderService.addRelyingPartyOverridesCollectionDefinitionsToJson(parsedJson["definitions"])
return ResponseEntity.ok(parsedJson)
}
catch (Exception e) {
log.error(e.getMessage(), e)
return ResponseEntity.status(INTERNAL_SERVER_ERROR)
.body([jsonParseError : e.getMessage(),
sourceUiSchemaDefinitionFile: this.jsonSchemaLocation.url])
}
}

@PostConstruct
void init() {
this.jsonSchemaLocation = nameIdFormatFilterSchema(this.jsonSchemaResourceLocationRegistry)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ 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
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.opensaml.OpenSamlNameIdFormatFilter
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.FilesystemMetadataResolver
Expand All @@ -28,13 +30,17 @@ import org.opensaml.saml.common.profile.logic.EntityIdPredicate
import org.opensaml.saml.metadata.resolver.MetadataResolver
import org.opensaml.saml.metadata.resolver.filter.MetadataFilter
import org.opensaml.saml.metadata.resolver.filter.MetadataFilterChain
import org.opensaml.saml.metadata.resolver.filter.impl.NameIDFormatFilter
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

import javax.annotation.Nonnull

import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY
import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.CONDITION_SCRIPT
import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.REGEX
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

Expand All @@ -60,7 +66,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
@Override
void reloadFilters(String metadataResolverResourceId) {
OpenSamlChainingMetadataResolver chainingMetadataResolver = (OpenSamlChainingMetadataResolver) metadataResolver
MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find { it.id == metadataResolverResourceId }
MetadataResolver targetMetadataResolver = chainingMetadataResolver.getResolvers().find {
it.id == metadataResolverResourceId
}
edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver jpaMetadataResolver = metadataResolverRepository.findByResourceId(metadataResolverResourceId)

if (targetMetadataResolver && targetMetadataResolver.getMetadataFilter() instanceof MetadataFilterChain) {
Expand Down Expand Up @@ -103,6 +111,30 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
target.setRules(rules)
metadataFilters.add(target)
}
if (metadataFilter instanceof NameIdFormatFilter) {
NameIdFormatFilter nameIdFormatFilter = NameIdFormatFilter.cast(metadataFilter)
NameIDFormatFilter openSamlTargetFilter = new OpenSamlNameIdFormatFilter()
openSamlTargetFilter.removeExistingFormats = nameIdFormatFilter.removeExistingFormats
Map<Predicate<EntityDescriptor>, Collection<String>> predicateRules = [:]
def type = nameIdFormatFilter.nameIdFormatFilterTarget.nameIdFormatFilterTargetType
def values = nameIdFormatFilter.nameIdFormatFilterTarget.value
switch (type) {
case ENTITY:
predicateRules[new EntityIdPredicate(values)] = nameIdFormatFilter.formats
break
case CONDITION_SCRIPT:
predicateRules[new ScriptedPredicate(new EvaluableScript(values[0]))] = nameIdFormatFilter.formats
break
case REGEX:
predicateRules[new ScriptedPredicate(new EvaluableScript(generateJavaScriptRegexScript(values[0])))] = nameIdFormatFilter.formats
break
default:
// do nothing, we'd have exploded elsewhere previously.
break
}
openSamlTargetFilter.rules = predicateRules
metadataFilters << openSamlTargetFilter
}
}
metadataFilterChain.setFilters(metadataFilters)
}
Expand Down Expand Up @@ -190,7 +222,7 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}

void constructXmlNodeForFilter(SignatureValidationFilter filter, def markupBuilderDelegate) {
if(filter.xmlShouldBeGenerated()) {
if (filter.xmlShouldBeGenerated()) {
markupBuilderDelegate.MetadataFilter(id: filter.name,
'xsi:type': 'SignatureValidation',
'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata',
Expand Down Expand Up @@ -261,14 +293,52 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}

void constructXmlNodeForFilter(RequiredValidUntilFilter filter, def markupBuilderDelegate) {
if(filter.xmlShouldBeGenerated()) {
if (filter.xmlShouldBeGenerated()) {
markupBuilderDelegate.MetadataFilter(
'xsi:type': 'RequiredValidUntil',
maxValidityInterval: filter.maxValidityInterval
)
}
}

void constructXmlNodeForFilter(NameIdFormatFilter filter, def markupBuilderDelegate) {
def type = filter.nameIdFormatFilterTarget.nameIdFormatFilterTargetType
markupBuilderDelegate.MetadataFilter(
'xsi:type': 'NameIDFormat',
'xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata',
'removeExistingFormats': filter.removeExistingFormats ?: null
) {
filter.formats.each {
Format(it)
}
switch (type) {
case ENTITY:
filter.nameIdFormatFilterTarget.value.each {
Entity(it)
}
break
case CONDITION_SCRIPT:
case REGEX:
ConditionScript() {
Script() {
def script
def scriptValue = filter.nameIdFormatFilterTarget.value[0]
if (type == CONDITION_SCRIPT) {
script = scriptValue
} else if (type == REGEX) {
script = generateJavaScriptRegexScript(scriptValue)
}
mkp.yieldUnescaped("\n<![CDATA[\n${script}\n]]>\n")
}
}
break
default:
// do nothing, we'd have exploded elsewhere previously.
break
}
}
}

void constructXmlNodeForResolver(FilesystemMetadataResolver resolver, def markupBuilderDelegate, Closure childNodes) {
markupBuilderDelegate.MetadataProvider(id: resolver.xmlId,
'xsi:type': 'FilesystemMetadataProvider',
Expand Down Expand Up @@ -486,4 +556,5 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.FILESYSTEM_METADATA_RESOLVER;
import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.LOCAL_DYNAMIC_METADATA_RESOLVER;
import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.DYNAMIC_HTTP_METADATA_RESOLVER;
import static edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocation.SchemaType.NAME_ID_FORMAT_FILTER;

/**
* @author Dmitriy Kopylenko
Expand Down Expand Up @@ -46,7 +47,12 @@ public class JsonSchemaComponentsConfiguration {
//Configured via @ConfigurationProperties (using setter method) with 'shibui.dynamic-http-metadata-provider-ui-schema-location' property and
// default value set here if that property is not explicitly set in application.properties
@Setter
private String dynamicHttpMetadataResolverUiSchemaLocation = "classpath:dynamic-http-metadata-provider.schema.json";
private String dynamicHttpMetadataResolverUiSchemaLocation = "classpath:nameid-filter.schema.json";

//Configured via @ConfigurationProperties (using setter method) with 'shibui.nameid-filter-ui-schema-location' property and
// default value set here if that property is not explicitly set in application.properties
@Setter
private String nameIdFormatFilterUiSchemaLocation = "classpath:nameid-filter.schema.json";

@Bean
public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(ResourceLoader resourceLoader, ObjectMapper jacksonMapper) {
Expand Down Expand Up @@ -80,6 +86,12 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res
.resourceLoader(resourceLoader)
.jacksonMapper(jacksonMapper)
.detectMalformedJson(true)
.build())
.register(NAME_ID_FORMAT_FILTER, JsonSchemaLocationBuilder.with()
.jsonSchemaLocation(nameIdFormatFilterUiSchemaLocation)
.resourceLoader(resourceLoader)
.jacksonMapper(jacksonMapper)
.detectMalformedJson(true)
.build());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
Expand Down Expand Up @@ -224,6 +225,13 @@ else if(filterWithUpdatedData instanceof RequiredValidUntilFilter) {
RequiredValidUntilFilter fromFilter = RequiredValidUntilFilter.class.cast(filterWithUpdatedData);
toFilter.setMaxValidityInterval(fromFilter.getMaxValidityInterval());
}
else if (filterWithUpdatedData instanceof NameIdFormatFilter) {
NameIdFormatFilter toFilter = NameIdFormatFilter.class.cast(filterToBeUpdated);
NameIdFormatFilter fromFilter = NameIdFormatFilter.class.cast(filterWithUpdatedData);
toFilter.setRemoveExistingFormats(fromFilter.getRemoveExistingFormats());
toFilter.setFormats(fromFilter.getFormats());
toFilter.setNameIdFormatFilterTarget(fromFilter.getNameIdFormatFilterTarget());
}
//TODO: add other types of concrete filters update here
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ public List<XMLObject> getOrderedChildren() {
return null;
}

//TODO: refactor mappings so we're also able persist and use non-null parent which happens to be used in some code paths
//where we use open saml API (re-filtering, for example)
@Transient
private transient XMLObject parent;
@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
@JsonSubTypes({@JsonSubTypes.Type(value=EntityRoleWhiteListFilter.class, name="EntityRoleWhiteList"),
@JsonSubTypes.Type(value=EntityAttributesFilter.class, name="EntityAttributes"),
@JsonSubTypes.Type(value=SignatureValidationFilter.class, name="SignatureValidation"),
@JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil")})
@JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil"),
@JsonSubTypes.Type(value=NameIdFormatFilter.class, name="NameIDFormat")})
public class MetadataFilter extends AbstractAuditable {

@JsonProperty("@type")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.filters;

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

import javax.persistence.CascadeType;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.OneToOne;
import javax.persistence.OrderColumn;
import java.util.List;

@Entity
@EqualsAndHashCode(callSuper = true)
@Getter
@Setter
@ToString
public class NameIdFormatFilter extends MetadataFilter {

public NameIdFormatFilter() {
type = "NameIDFormat";
}

private Boolean removeExistingFormats = false;

@ElementCollection
@OrderColumn
private List<String> formats;

@OneToOne(cascade = CascadeType.ALL)
private NameIdFormatFilterTarget nameIdFormatFilterTarget;

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

import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable;
import lombok.EqualsAndHashCode;
import lombok.ToString;

import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.OrderColumn;
import java.util.ArrayList;
import java.util.List;

@Entity
@EqualsAndHashCode(callSuper = true)
@ToString
public class NameIdFormatFilterTarget extends AbstractAuditable {

public enum NameIdFormatFilterTargetType {
ENTITY, CONDITION_SCRIPT, REGEX
}

private NameIdFormatFilterTargetType nameIdFormatFilterTargetType;

public NameIdFormatFilterTargetType getNameIdFormatFilterTargetType() {
return nameIdFormatFilterTargetType;
}

public void setNameIdFormatFilterTargetType(NameIdFormatFilterTargetType nameIdFormatFilterTargetType) {
this.nameIdFormatFilterTargetType = nameIdFormatFilterTargetType;
}

@ElementCollection
@OrderColumn
private List<String> value;

public List<String> getValue() {
return value;
}

public void setSingleValue(String value) {
List<String> values = new ArrayList<>();
values.add(value);
this.value = values;
}

public void setValue(List<String> value) {
this.value = value;
}


}
Loading

0 comments on commit 0260d59

Please sign in to comment.