From 204790ba87dac41649cdbdf9304ba4aee30e8f23 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Fri, 12 Oct 2018 22:04:58 -0700 Subject: [PATCH] [SHIBUI-906] First implementation of custom relying party overrides. Does not include validation of input JSON. Does not include unit test fixes. Everything else should work, though. In theory. --- ...tadataSourcesUiDefinitionController.groovy | 7 +- .../CoreShibUiConfiguration.java | 10 +- .../filters/EntityAttributesFilter.java | 7 +- .../EntityDescriptorRepresentation.java | 7 +- .../domain/frontend/FilterRepresentation.java | 7 +- .../ui/service/EntityDescriptorService.java | 3 +- .../admin/ui/service/EntityService.java | 3 +- .../JPAEntityDescriptorServiceImpl.java | 54 +----- .../ui/service/JPAEntityServiceImpl.java | 83 +++++---- .../util/ModelRepresentationConversions.java | 163 ++++++++++-------- backend/src/main/resources/application.yml | 28 ++- 11 files changed, 195 insertions(+), 177 deletions(-) diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy index 9d790f362..3600667ba 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataSourcesUiDefinitionController.groovy @@ -1,8 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller import com.fasterxml.jackson.databind.ObjectMapper -import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomAttributesConfiguration -import groovy.json.JsonOutput +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.core.io.ResourceLoader @@ -37,7 +36,7 @@ class MetadataSourcesUiDefinitionController { ObjectMapper jacksonObjectMapper @Autowired - CustomAttributesConfiguration customAttributesConfiguration + CustomPropertiesConfiguration customPropertiesConfiguration @GetMapping ResponseEntity getUiDefinitionJsonSchema() { @@ -45,7 +44,7 @@ class MetadataSourcesUiDefinitionController { def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaUrl, Map) def widget = parsedJson["properties"]["attributeRelease"]["widget"] def data = [] - customAttributesConfiguration.getAttributes().each { + customPropertiesConfiguration.getAttributes().each { def attribute = [:] attribute["key"] = it["name"] attribute["label"] = it["displayName"] diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index b4a8adc42..46b05710e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -24,13 +24,14 @@ import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.util.AttributeUtility; import edu.internet2.tier.shibboleth.admin.util.LuceneUtility; +import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions; import org.apache.lucene.analysis.Analyzer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; @@ -172,7 +173,7 @@ public LuceneUtility luceneUtility(DirectoryService directoryService) { } @Bean - public CustomPropertiesConfiguration customAttributesConfiguration() { + public CustomPropertiesConfiguration customPropertiesConfiguration() { return new CustomPropertiesConfiguration(); } @@ -180,4 +181,9 @@ public CustomPropertiesConfiguration customAttributesConfiguration() { public Module stringTrimModule() { return new StringTrimModule(); } + + @Bean + public ModelRepresentationConversions modelRepresentationConversions() { + return new ModelRepresentationConversions(customPropertiesConfiguration()); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index 8492e745d..86a2181c7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute; -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -16,8 +15,8 @@ import javax.persistence.PostLoad; import javax.persistence.Transient; import java.util.ArrayList; - import java.util.List; +import java.util.Map; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromAttributeReleaseList; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromRelyingPartyOverridesRepresentation; @@ -52,9 +51,9 @@ public void setAttributeRelease(List attributeRelease) { } @Transient - private RelyingPartyOverridesRepresentation relyingPartyOverrides; + private Map relyingPartyOverrides; - public void setRelyingPartyOverrides(RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation) { + public void setRelyingPartyOverrides(Map relyingPartyOverridesRepresentation) { this.relyingPartyOverrides = relyingPartyOverridesRepresentation; this.rebuildAttributes(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java index c684d458a..b879e76d5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class EntityDescriptorRepresentation implements Serializable { @@ -58,7 +59,7 @@ public EntityDescriptorRepresentation(String id, private LocalDateTime modifiedDate; - private RelyingPartyOverridesRepresentation relyingPartyOverrides; + private Map relyingPartyOverrides; private List attributeRelease; @@ -180,11 +181,11 @@ public void setModifiedDate(LocalDateTime modifiedDate) { this.modifiedDate = modifiedDate; } - public RelyingPartyOverridesRepresentation getRelyingPartyOverrides() { + public Map getRelyingPartyOverrides() { return relyingPartyOverrides; } - public void setRelyingPartyOverrides(RelyingPartyOverridesRepresentation relyingPartyOverrides) { + public void setRelyingPartyOverrides(Map relyingPartyOverrides) { this.relyingPartyOverrides = relyingPartyOverrides; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java index b9e2f1213..0276a2b9c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/FilterRepresentation.java @@ -3,13 +3,14 @@ import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; public class FilterRepresentation implements Serializable { private String id; private String filterName; private boolean filterEnabled; private FilterTargetRepresentation filterTarget; - private RelyingPartyOverridesRepresentation relyingPartyOverrides; + private Map relyingPartyOverrides; private List attributeRelease; private LocalDateTime createdDate; private LocalDateTime modifiedDate; @@ -57,11 +58,11 @@ public void setFilterTarget(FilterTargetRepresentation filterTarget) { this.filterTarget = filterTarget; } - public RelyingPartyOverridesRepresentation getRelyingPartyOverrides() { + public Map getRelyingPartyOverrides() { return relyingPartyOverrides; } - public void setRelyingPartyOverrides(RelyingPartyOverridesRepresentation relyingPartyOverrides) { + public void setRelyingPartyOverrides(Map relyingPartyOverrides) { this.relyingPartyOverrides = relyingPartyOverrides; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java index 086c65f64..a78651964 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java @@ -6,6 +6,7 @@ import org.opensaml.saml.saml2.metadata.EntityDescriptor; import java.util.List; +import java.util.Map; /** * Main backend facade API that defines operations pertaining to manipulating {@link EntityDescriptor} state. @@ -52,6 +53,6 @@ public interface EntityDescriptorService { * @param attributeList the list of attributes to generate from * @return a RelyingPartyOverridesRepresentation based on the given list of attributes */ - RelyingPartyOverridesRepresentation getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList); + Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java index 5a4a65a25..b85473e78 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityService.java @@ -5,6 +5,7 @@ import org.opensaml.saml.saml2.core.Attribute; import java.util.List; +import java.util.Map; /** * facade API that defines operations for creating various entities from JSON representations @@ -13,5 +14,5 @@ public interface EntityService { List getAttributeListFromEntityRepresentation(EntityDescriptorRepresentation entityDescriptorRepresentation); edu.internet2.tier.shibboleth.admin.ui.domain.Attribute getAttributeFromAttributeReleaseList(List attributeReleaseList); List getAttributeListFromAttributeReleaseList(List attributeReleaseList); - List getAttributeListFromRelyingPartyOverridesRepresentation(RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation); + List getAttributeListFromRelyingPartyOverridesRepresentation(Map relyingPartyOverridesRepresentation); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index a0ec7e1b6..9ca15a77e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -54,7 +54,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getBooleanValueOfAttribute; @@ -489,54 +491,16 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope // set up extensions if (ed.getExtensions() != null && ed.getExtensions().getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME) != null && ed.getExtensions().getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).size() == 1) { // we have entity attributes (hopefully), so should have overrides - RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation = new RelyingPartyOverridesRepresentation(); - representation.setRelyingPartyOverrides(relyingPartyOverridesRepresentation); + Map relyingPartyOverrides = new HashMap<>(); for (org.opensaml.saml.saml2.core.Attribute attribute : ((EntityAttributes) ed.getExtensions().getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).get(0)).getAttributes()) { Attribute jpaAttribute = (Attribute) attribute; - // TODO: this is going to get real ugly real quick. clean it up, future Jj! - switch (jpaAttribute.getName()) { - case MDDCConstants.SIGN_ASSERTIONS: - relyingPartyOverridesRepresentation.setSignAssertion(getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.SIGN_RESPONSES: - relyingPartyOverridesRepresentation.setDontSignResponse(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.ENCRYPT_ASSERTIONS: - relyingPartyOverridesRepresentation.setTurnOffEncryption(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.SECURITY_CONFIGURATION: - if (getStringListValueOfAttribute(jpaAttribute).contains("shibboleth.SecurityConfiguration.SHA1")) { - relyingPartyOverridesRepresentation.setUseSha(true); - } - break; - case MDDCConstants.DISALLOWED_FEATURES: - if ((Integer.decode(getStringListValueOfAttribute(jpaAttribute).get(0)) & 0x1) == 0x1) { - relyingPartyOverridesRepresentation.setIgnoreAuthenticationMethod(true); - } - break; - case MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE: - relyingPartyOverridesRepresentation.setOmitNotBefore(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.RESPONDER_ID: - relyingPartyOverridesRepresentation.setResponderId(getStringListValueOfAttribute(jpaAttribute).get(0)); - break; - case MDDCConstants.NAME_ID_FORMAT_PRECEDENCE: - relyingPartyOverridesRepresentation.setNameIdFormats(getStringListValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.DEFAULT_AUTHENTICATION_METHODS: - relyingPartyOverridesRepresentation.setAuthenticationMethods(getStringListValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.RELEASE_ATTRIBUTES: - representation.setAttributeRelease(getStringListOfAttributeValues(attribute.getAttributeValues())); - break; - case MDDCConstants.FORCE_AUTHN: - relyingPartyOverridesRepresentation.setForceAuthn(getBooleanValueOfAttribute(jpaAttribute)); - break; - default: - break; - } + + relyingPartyOverrides.put(ModelRepresentationConversions.getAttributeNameFromFriendlyName(jpaAttribute.getFriendlyName()), + jpaAttribute.getAttributeValues()); } + + representation.setRelyingPartyOverrides(relyingPartyOverrides); } return representation; @@ -548,7 +512,7 @@ public List getAttributeReleaseListFromAttributeList(List att } @Override - public RelyingPartyOverridesRepresentation getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { + public Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { return ModelRepresentationConversions.getRelyingPartyOverridesRepresentationFromAttributeList(attributeList); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java index e8b4ce590..db673bb66 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityServiceImpl.java @@ -1,18 +1,23 @@ package edu.internet2.tier.shibboleth.admin.ui.service; +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration; import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBuilder; import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeValue; +import edu.internet2.tier.shibboleth.admin.ui.domain.RelyingPartyOverrideProperty; import edu.internet2.tier.shibboleth.admin.ui.domain.XSString; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation; import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.util.AttributeUtility; import edu.internet2.tier.shibboleth.admin.util.MDDCConstants; +import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions; import org.opensaml.saml.saml2.core.Attribute; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; public class JPAEntityServiceImpl implements EntityService { @@ -22,6 +27,9 @@ public class JPAEntityServiceImpl implements EntityService { @Autowired private AttributeUtility attributeUtility; + @Autowired + private CustomPropertiesConfiguration customPropertiesConfiguration = new CustomPropertiesConfiguration(); + public JPAEntityServiceImpl(OpenSamlObjects openSamlObjects) { this.openSamlObjects = openSamlObjects; } @@ -81,44 +89,51 @@ public List getAttributeListFromAttributeReleaseList(List att } @Override - public List getAttributeListFromRelyingPartyOverridesRepresentation(RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation) { + public List getAttributeListFromRelyingPartyOverridesRepresentation(Map relyingPartyOverridesRepresentation) { + List overridePropertyList = customPropertiesConfiguration.getOverrides(); List list = new ArrayList<>(); - if (relyingPartyOverridesRepresentation != null) { - if (relyingPartyOverridesRepresentation.isSignAssertion()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.SIGN_ASSERTIONS, MDDCConstants.SIGN_ASSERTIONS_FN, true)); - } - if (relyingPartyOverridesRepresentation.isDontSignResponse()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.SIGN_RESPONSES, MDDCConstants.SIGN_RESPONSES_FN, false)); - } - if (relyingPartyOverridesRepresentation.isTurnOffEncryption()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.ENCRYPT_ASSERTIONS, MDDCConstants.ENCRYPT_ASSERTIONS_FN, false)); - } - if (relyingPartyOverridesRepresentation.isUseSha()) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.SECURITY_CONFIGURATION, MDDCConstants.SECURITY_CONFIGURATION_FN, "shibboleth.SecurityConfiguration.SHA1")); - } - if (relyingPartyOverridesRepresentation.isIgnoreAuthenticationMethod()) { - // this is actually going to be wrong, but it will work for the time being. this should be a bitmask value that we calculate - // TODO: fix - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.DISALLOWED_FEATURES, MDDCConstants.DISALLOWED_FEATURES_FN, "0x1")); - } - if (relyingPartyOverridesRepresentation.isOmitNotBefore()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE, MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE_FN, false)); - } - if (relyingPartyOverridesRepresentation.getResponderId() != null && !"".equals(relyingPartyOverridesRepresentation.getResponderId())) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.RESPONDER_ID, MDDCConstants.RESPONDER_ID_FN, relyingPartyOverridesRepresentation.getResponderId())); - } - if (relyingPartyOverridesRepresentation.getNameIdFormats() != null && relyingPartyOverridesRepresentation.getNameIdFormats().size() > 0) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.NAME_ID_FORMAT_PRECEDENCE, MDDCConstants.NAME_ID_FORMAT_PRECEDENCE_FN, relyingPartyOverridesRepresentation.getNameIdFormats())); - } - if (relyingPartyOverridesRepresentation.getAuthenticationMethods() != null && relyingPartyOverridesRepresentation.getAuthenticationMethods().size() > 0) { - list.add(attributeUtility.createAttributeWithArbitraryValues(MDDCConstants.DEFAULT_AUTHENTICATION_METHODS, MDDCConstants.DEFAULT_AUTHENTICATION_METHODS_FN, relyingPartyOverridesRepresentation.getAuthenticationMethods())); - } - if (relyingPartyOverridesRepresentation.isForceAuthn()) { - list.add(attributeUtility.createAttributeWithBooleanValue(MDDCConstants.FORCE_AUTHN, MDDCConstants.FORCE_AUTHN_FN, true)); + for (Map.Entry entry : relyingPartyOverridesRepresentation.entrySet()) { + String key = (String) entry.getKey(); + RelyingPartyOverrideProperty overrideProperty = overridePropertyList.stream().filter(op -> op.getDisplayName().equals(key)).findFirst().get(); + switch (ModelRepresentationConversions.AttributeTypes.valueOf(overrideProperty.getDisplayType().toUpperCase())) { + case BOOLEAN: + if (!overrideProperty.getPersistType().equalsIgnoreCase("boolean")) { + // we must be persisting a string then + list.add(attributeUtility.createAttributeWithStringValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + (String) entry.getValue())); + } else { + list.add(attributeUtility.createAttributeWithBooleanValue(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + Boolean.valueOf((String) entry.getValue()))); + } + break; + case INTEGER: + list.add(attributeUtility.createAttributeWithIntegerValue(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + Integer.valueOf((String) entry.getValue()))); + break; + case STRING: + list.add(attributeUtility.createAttributeWithStringValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + (String) entry.getValue())); + break; + case SET: + list.add(attributeUtility.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + (Set) entry.getValue())); + break; + case LIST: + list.add(attributeUtility.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + (List) entry.getValue())); + break; + default: + throw new UnsupportedOperationException("getAttributeListFromRelyingPartyOverridesRepresentation was called with an unsupported type (" + overrideProperty.getDisplayType() + ")!"); } } - return (List)(List)list; + return (List) (List) list; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/ModelRepresentationConversions.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/ModelRepresentationConversions.java index 05f595a2b..13d4d6587 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/ModelRepresentationConversions.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/ModelRepresentationConversions.java @@ -1,26 +1,39 @@ package edu.internet2.tier.shibboleth.admin.util; +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration; import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute; import edu.internet2.tier.shibboleth.admin.ui.domain.RelyingPartyOverrideProperty; +import edu.internet2.tier.shibboleth.admin.ui.domain.XSAny; import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean; -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.RelyingPartyOverridesRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.XSInteger; +import edu.internet2.tier.shibboleth.admin.ui.domain.XSString; import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import org.opensaml.core.xml.XMLObject; +import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * Utility class to deal with model conversions related functionality */ -public abstract class ModelRepresentationConversions { +public class ModelRepresentationConversions { private static final AttributeUtility ATTRIBUTE_UTILITY; + private static CustomPropertiesConfiguration customPropertiesConfiguration; + + @Autowired + public ModelRepresentationConversions(CustomPropertiesConfiguration customPropertiesConfiguration) { + ModelRepresentationConversions.customPropertiesConfiguration = customPropertiesConfiguration; + } + static { OpenSamlObjects openSamlObjects = new OpenSamlObjects(); try { @@ -65,53 +78,55 @@ public static List getStringListValueOfAttribute(Attribute attribute) { return getStringListOfAttributeValues(attribute.getAttributeValues()); } - public static RelyingPartyOverridesRepresentation getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { - RelyingPartyOverridesRepresentation relyingPartyOverridesRepresentation = new RelyingPartyOverridesRepresentation(); + public static String getAttributeNameFromFriendlyName(String attributeFriendlyName) { + Optional override = customPropertiesConfiguration.getOverrides().stream().filter(it -> it.getAttributeFriendlyName().equals(attributeFriendlyName)).findFirst(); + if (!override.isPresent()) { + // WAT? Somehow we persisted a property that we're not configured for. This shouldn't happen. + throw new RuntimeException("Persisted attribute with friendlyName \"" + attributeFriendlyName + "\" doesn't have a matching configuration in application.yml!"); + } + return ((RelyingPartyOverrideProperty)override.get()).getName(); + } + + public static Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { + Map relyingPartyOverrides = new HashMap<>(); for (org.opensaml.saml.saml2.core.Attribute attribute : attributeList) { Attribute jpaAttribute = (Attribute) attribute; - // TODO: this is going to get real ugly real quick. clean it up, future Jj! - switch (jpaAttribute.getName()) { - case MDDCConstants.SIGN_ASSERTIONS: - relyingPartyOverridesRepresentation.setSignAssertion(getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.SIGN_RESPONSES: - relyingPartyOverridesRepresentation.setDontSignResponse(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.ENCRYPT_ASSERTIONS: - relyingPartyOverridesRepresentation.setTurnOffEncryption(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.SECURITY_CONFIGURATION: - if (getStringListValueOfAttribute(jpaAttribute).contains("shibboleth.SecurityConfiguration.SHA1")) { - relyingPartyOverridesRepresentation.setUseSha(true); - } - break; - case MDDCConstants.DISALLOWED_FEATURES: - if ((Integer.decode(getStringListValueOfAttribute(jpaAttribute).get(0)) & 0x1) == 0x1) { - relyingPartyOverridesRepresentation.setIgnoreAuthenticationMethod(true); - } - break; - case MDDCConstants.INCLUDE_CONDITIONS_NOT_BEFORE: - relyingPartyOverridesRepresentation.setOmitNotBefore(!getBooleanValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.RESPONDER_ID: - relyingPartyOverridesRepresentation.setResponderId(getStringListValueOfAttribute(jpaAttribute).get(0)); - break; - case MDDCConstants.NAME_ID_FORMAT_PRECEDENCE: - relyingPartyOverridesRepresentation.setNameIdFormats(getStringListValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.DEFAULT_AUTHENTICATION_METHODS: - relyingPartyOverridesRepresentation.setAuthenticationMethods(getStringListValueOfAttribute(jpaAttribute)); - break; - case MDDCConstants.FORCE_AUTHN: - relyingPartyOverridesRepresentation.setForceAuthn(getBooleanValueOfAttribute(jpaAttribute)); - break; - default: - break; - } + + relyingPartyOverrides.put(getAttributeNameFromFriendlyName(jpaAttribute.getFriendlyName()), + getOverrideFromAttribute(jpaAttribute)); } - return relyingPartyOverridesRepresentation; + return relyingPartyOverrides; + } + + public static Object getOverrideFromAttribute(Attribute attribute) { + RelyingPartyOverrideProperty relyingPartyOverrideProperty = customPropertiesConfiguration.getOverrides().stream() + .filter(it -> it.getAttributeFriendlyName().equals(attribute.getFriendlyName())).findFirst().get(); + + List attributeValues = attribute.getAttributeValues(); + switch(AttributeTypes.valueOf(relyingPartyOverrideProperty.getDisplayType().toUpperCase())) { + case BOOLEAN: + if (relyingPartyOverrideProperty.getPersistType() != null + && (!relyingPartyOverrideProperty.getPersistType().equalsIgnoreCase("boolean"))) { + return "true"; + } else { + return ((XSBoolean) attributeValues.get(0)).getStoredValue(); + } + case INTEGER: + return ((XSInteger) attributeValues.get(0)).getValue(); + case STRING: + if (attributeValues.get(0) instanceof XSAny) { + return ((XSAny) attributeValues.get(0)).getTextContent(); + } else { + return ((XSString) attributeValues.get(0)).getValue(); + } + case LIST: + case SET: + return attributeValues.stream().map(it -> ((XSAny) it).getTextContent()).collect(Collectors.toList()); + default: + throw new UnsupportedOperationException("An unsupported persist type was specified (" + relyingPartyOverrideProperty.getPersistType() + ")!"); + } } public static List getAttributeListFromAttributeReleaseList(List attributeReleaseList) { @@ -125,30 +140,31 @@ public static List getAttributeListFromA } public static List getAttributeListFromRelyingPartyOverridesRepresentation - (List overridePropertyList, - Map relyingPartyOverridesRepresentation) { + (Map relyingPartyOverridesRepresentation) { + List overridePropertyList = customPropertiesConfiguration.getOverrides(); List list = new ArrayList<>(); for (Map.Entry entry : relyingPartyOverridesRepresentation.entrySet()) { String key = (String) entry.getKey(); - RelyingPartyOverrideProperty overrideProperty = overridePropertyList.stream().filter(op -> op.getDisplayName().equals(key)).findFirst().get(); - switch (AttributeTypes.valueOf(overrideProperty.getDisplayType())) { + RelyingPartyOverrideProperty overrideProperty = overridePropertyList.stream().filter(op -> op.getName().equals(key)).findFirst().get(); + switch (AttributeTypes.valueOf(overrideProperty.getDisplayType().toUpperCase())) { case BOOLEAN: - if (!overrideProperty.getPersistType().equals("boolean")) { + if (overrideProperty.getPersistType() != null && + !overrideProperty.getPersistType().equalsIgnoreCase("boolean")) { // we must be persisting a string then list.add(ATTRIBUTE_UTILITY.createAttributeWithStringValues(overrideProperty.getAttributeName(), overrideProperty.getAttributeFriendlyName(), - (String) entry.getValue()); + overrideProperty.getPersistValue())); } else { list.add(ATTRIBUTE_UTILITY.createAttributeWithBooleanValue(overrideProperty.getAttributeName(), overrideProperty.getAttributeFriendlyName(), - Boolean.valueOf((String) entry.getValue())); + (Boolean) entry.getValue())); } break; case INTEGER: list.add(ATTRIBUTE_UTILITY.createAttributeWithIntegerValue(overrideProperty.getAttributeName(), overrideProperty.getAttributeFriendlyName(), - Integer.valueOf((String) entry.getValue()))); + (Integer) entry.getValue())); break; case STRING: list.add(ATTRIBUTE_UTILITY.createAttributeWithStringValues(overrideProperty.getAttributeName(), @@ -156,14 +172,20 @@ public static List getAttributeListFromA (String) entry.getValue())); break; case SET: - list.add(ATTRIBUTE_UTILITY.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), - overrideProperty.getAttributeFriendlyName(), - (Set) entry.getValue())); + Set setValues = (Set) entry.getValue(); + if (setValues.size() > 0) { + list.add(ATTRIBUTE_UTILITY.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + setValues)); + } break; case LIST: - list.add(ATTRIBUTE_UTILITY.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), - overrideProperty.getAttributeFriendlyName(), - (List) entry.getValue())); + List listValues = (List) entry.getValue(); + if (listValues.size() > 0) { + list.add(ATTRIBUTE_UTILITY.createAttributeWithArbitraryValues(overrideProperty.getAttributeName(), + overrideProperty.getAttributeFriendlyName(), + listValues)); + } break; default: throw new UnsupportedOperationException("getAttributeListFromRelyingPartyOverridesRepresentation was called with an unsupported type (" + overrideProperty.getDisplayType() + ")!"); @@ -174,22 +196,11 @@ public static List getAttributeListFromA } - private enum AttributeTypes { - BOOLEAN("boolean"), - INTEGER("integer"), - STRING("string"), - SET("set"), - LIST("list"); - - private final String type; - - AttributeTypes(final String type) { - this.type = type; - } - - @Override - public String toString() { - return type; - } - } + public enum AttributeTypes { + BOOLEAN, + INTEGER, + STRING, + SET, + LIST + } } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 9a5fbaa87..5fbbf5c69 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -31,7 +31,9 @@ custom: # The structure of an entry is as follows: # - name: The name of the entry. used to uniquely identify this entry. # displayName: This will normally be the label used when displaying this override in the UI +# displayType: The type to use when displaying this option # helpText: This is the help-icon hover-over text +# defaultValues: One or more values to be displayed as default options in the UI # persistType: Optional. If it is necessary to persist something different than the override's display type, # set that type here. For example, display a boolean, but persist a string. # persistValue: Required only when persistType is used. Defines the value to be persisted. @@ -49,36 +51,48 @@ custom: displayName: Sign the Assertion displayType: boolean helpText: Sign Assertion - attributeName: - attributeFriendlyName: - - name: signResponses + attributeName: http://shibboleth.net/ns/profiles/saml2/sso/browser/signAssertions + attributeFriendlyName: signAssertions + - name: dontSignResponse displayName: Don't Sign the Response displayType: boolean helpText: Don't Sign Response + attributeName: http://shibboleth.net/ns/profiles/saml2/sso/browser/signResponses + attributeFriendlyName: signResponses - name: turnOffEncryption displayName: Turn Off Encryption of Response displayType: boolean helpText: Turn Off Encryption of Response + attributeName: http://shibboleth.net/ns/profiles/encryptAssertions + attributeFriendlyName: encryptAssertions - name: useSha displayName: Use SHA1 Signing Algorithm displayType: boolean helpText: Use SHA1 Signing Algorithm persistType: string persistValue: shibboleth.SecurityConfiguration.SHA1 + attributeName: http://shibboleth.net/ns/profiles/securityConfiguration + attributeFriendlyName: securityConfiguration - name: ignoreAuthenticationMethod displayName: Ignore any SP-Requested Authentication Method displayType: boolean helpText: Ignore any SP-Requested Authentication Method persistType: string persistValue: 0x1 + attributeName: http://shibboleth.net/ns/profiles/disallowedFeatures + attributeFriendlyName: disallowedFeatures - name: omitNotBefore displayName: Omit Not Before Condition displayType: boolean helpText: Omit Not Before Condition + attributeName: http://shibboleth.net/ns/profiles/includeConditionsNotBefore + attributeFriendlyName: includeConditionsNotBefore - name: responderId displayName: responderId displayType: string helpText: ResponderId + attributeName: http://shibboleth.net/ns/profiles/responderId + attributeFriendlyName: responderId - name: nameIdFormats displayName: nameIdFormats displayType: list @@ -88,6 +102,8 @@ custom: - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - urn:oasis:names:tc:SAML:2.0:nameid-format:transient + attributeName: http://shibboleth.net/ns/profiles/nameIDFormatPrecedence + attributeFriendlyName: nameIDFormatPrecedence - name: authenticationMethods displayName: authenticationMethods displayType: list @@ -96,7 +112,11 @@ custom: - https://refeds.org/profile/mfa - urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken - urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + attributeName: http://shibboleth.net/ns/profiles/defaultAuthenticationMethods + attributeFriendlyName: defaultAuthenticationMethods - name: forceAuthn displayName: Force AuthN displayType: boolean - helpText: Disallows use (or reuse) of authentication results and login flows that don't provide a real-time proof of user presence in the login process \ No newline at end of file + helpText: Disallows use (or reuse) of authentication results and login flows that don't provide a real-time proof of user presence in the login process + attributeName: http://shibboleth.net/ns/profiles/forceAuthn + attributeFriendlyName: forceAuthn \ No newline at end of file