diff --git a/backend/build.gradle b/backend/build.gradle index 83d2c8dd8..07669a2d9 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -135,6 +135,14 @@ dependencies { compile "net.shibboleth.idp:${it}:${project.'shibbolethVersion'}" } + // Shib OIDC + ['metadata', 'profile', 'crypto'].each { + testCompile "net.shibboleth.oidc:oidc-common-${it}-api:2.1.0" + testCompile "net.shibboleth.oidc:oidc-common-${it}-impl:2.1.0" + } + + implementation "net.shibboleth.oidc:oidc-common-saml-api:2.1.0" + // hibernate deps ['hibernate-core'].each { compile "org.hibernate:${it}:${project.'hibernateVersion'}" @@ -337,6 +345,12 @@ task generateSources { processLine(it['@className'].toString(), 'src/main/templates/SignatureBuilderTemplate.java') } } + + new XmlSlurper().parse(file('src/main/resources/jpa-shib-oidc-config.xml')).with { builders -> + builders.ObjectProviders.ObjectProvider.BuilderClass.each { + processLine(it['@className'].toString(), 'src/main/templates/OIDCBuilderTemplate.java') + } + } } } 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 db00c31d3..fa21d8a31 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 @@ -45,6 +45,7 @@ class MetadataSourcesUiDefinitionController { JsonSchemaBuilderService jsonSchemaBuilderService @GetMapping + // TODO - CHARLES add type ( SAML|OIDC ) variable to return the correct one - default to saml... ResponseEntity getUiDefinitionJsonSchema() { try { def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java index b86c86b1d..133c81ff5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; import org.hibernate.envers.Audited; import javax.annotation.Nullable; @@ -18,6 +19,7 @@ public class Description extends AbstractXMLObject implements org.opensaml.saml. @Column(name = "descriptionValue") @Lob + @Type(type = "org.hibernate.type.TextType") private String value; @Nullable diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java index cff1a3c9d..585f5220a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.ValueXMLObject; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.envers.Audited; @@ -12,7 +13,7 @@ @Entity @EqualsAndHashCode(callSuper = true) @Audited -public class X509Certificate extends AbstractXMLObject implements org.opensaml.xmlsec.signature.X509Certificate { +public class X509Certificate extends AbstractXMLObject implements ValueXMLObject, org.opensaml.xmlsec.signature.X509Certificate { @Column(name = "x509CertificateValue") @Lob @Type(type = "org.hibernate.type.TextType") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java index 7afd88814..40e80085d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java @@ -97,4 +97,4 @@ public List getOrderedChildren() { return children; } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java index de81fcdf6..a9225b327 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java @@ -1,19 +1,22 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; import org.hibernate.envers.Audited; import org.opensaml.core.xml.util.AttributeMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.persistence.Entity; +import javax.persistence.Lob; import javax.persistence.Transient; @Entity @EqualsAndHashCode(callSuper = true, exclude = {"unknownAttributes"}) @Audited public class XSAny extends AbstractElementExtensibleXMLObject implements org.opensaml.core.xml.schema.XSAny { - + @Lob + @Type(type = "org.hibernate.type.TextType") private String textContext; //TODO: implement. this at the underlying level is a just a Map @@ -40,4 +43,4 @@ public void setTextContent(@Nullable String newContent) { public AttributeMap getUnknownAttributes() { return this.unknownAttributes; } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/KeyDescriptorRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/KeyDescriptorRepresentation.java index f56f5ad5b..aa6c564ea 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/KeyDescriptorRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/KeyDescriptorRepresentation.java @@ -1,38 +1,21 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.frontend; +import lombok.Getter; +import lombok.Setter; + import java.io.Serializable; +@Getter +@Setter public class KeyDescriptorRepresentation implements Serializable { - private static final long serialVersionUID = -2397547851045884034L; - private boolean x509CertificateAvailable; - - private boolean authenticationRequestsSigned; - - private String x509Certificate; - - public boolean isX509CertificateAvailable() { - return x509CertificateAvailable; - } - - public void setX509CertificateAvailable(boolean x509CertificateAvailable) { - this.x509CertificateAvailable = x509CertificateAvailable; - } - - public boolean isAuthenticationRequestsSigned() { - return authenticationRequestsSigned; - } - - public void setAuthenticationRequestsSigned(boolean authenticationRequestsSigned) { - this.authenticationRequestsSigned = authenticationRequestsSigned; - } - - public String getX509Certificate() { - return x509Certificate; - } + private String name; + private String value; + private String type; + private ElementType elementType; - public void setX509Certificate(String x509Certificate) { - this.x509Certificate = x509Certificate; + public enum ElementType { + jwksData, jwksUri, clientSecret, clientSecretKeyReference, X509Data, unsupported } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/SecurityInfoRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/SecurityInfoRepresentation.java index c2e5a2f9f..7ba875678 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/SecurityInfoRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/SecurityInfoRepresentation.java @@ -1,86 +1,35 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.frontend; +import lombok.Getter; +import lombok.Setter; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; +@Getter +@Setter public class SecurityInfoRepresentation implements Serializable { - private static final long serialVersionUID = 9016350010045719454L; - private boolean x509CertificateAvailable; - private boolean authenticationRequestsSigned; - private boolean wantAssertionsSigned; - private List x509Certificates = new ArrayList<>(); + private List keyDescriptors = new ArrayList<>(); - public boolean isX509CertificateAvailable() { - return x509CertificateAvailable; - } - - public void setX509CertificateAvailable(boolean x509CertificateAvailable) { - this.x509CertificateAvailable = x509CertificateAvailable; - } - - public boolean isAuthenticationRequestsSigned() { - return authenticationRequestsSigned; - } - - public void setAuthenticationRequestsSigned(boolean authenticationRequestsSigned) { - this.authenticationRequestsSigned = authenticationRequestsSigned; - } - - public boolean isWantAssertionsSigned() { - return wantAssertionsSigned; - } - - public void setWantAssertionsSigned(boolean wantAssertionsSigned) { - this.wantAssertionsSigned = wantAssertionsSigned; - } - - public List getX509Certificates() { - return x509Certificates; - } - - public void setX509Certificates(List x509Certificates) { - this.x509Certificates = x509Certificates; + public void addKeyDescriptor(KeyDescriptorRepresentation keyDescriptorRep) { + keyDescriptors.add(keyDescriptorRep); } + @Getter + @Setter + @Deprecated public static class X509CertificateRepresentation implements Serializable { - private static final long serialVersionUID = -4893206348572998788L; private String name; - + private String value; //TODO refactor into Enum? private String type; - - private String value; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/AbstractValueXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/AbstractValueXMLObject.java new file mode 100644 index 000000000..d4416b6e7 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/AbstractValueXMLObject.java @@ -0,0 +1,30 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractXMLObject; +import lombok.EqualsAndHashCode; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; + +import javax.annotation.Nullable; +import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@EqualsAndHashCode(callSuper = true) +@Audited +@AuditOverride(forClass = AbstractXMLObject.class) +public abstract class AbstractValueXMLObject extends AbstractXMLObject implements ValueXMLObject { + private String stringValue; + + @Nullable + public String getValue() { + return this.stringValue; + } + + public void setValue(@Nullable String newValue) { + this.stringValue = newValue; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecret.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecret.java new file mode 100644 index 000000000..9e41947e2 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecret.java @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Audited +public class ClientSecret extends AbstractValueXMLObject implements net.shibboleth.oidc.saml.xmlobject.ClientSecret { +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecretKeyReference.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecretKeyReference.java new file mode 100644 index 000000000..ccce90e3c --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ClientSecretKeyReference.java @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Audited +public class ClientSecretKeyReference extends AbstractValueXMLObject implements net.shibboleth.oidc.saml.xmlobject.ClientSecretKeyReference { +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksData.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksData.java new file mode 100644 index 000000000..30f27faec --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksData.java @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Audited +public class JwksData extends AbstractValueXMLObject implements net.shibboleth.oidc.saml.xmlobject.JwksData { +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksUri.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksUri.java new file mode 100644 index 000000000..7322d083a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/JwksUri.java @@ -0,0 +1,12 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; + +import javax.persistence.Entity; + +@Entity +@EqualsAndHashCode(callSuper = true) +@Audited +public class JwksUri extends AbstractValueXMLObject implements net.shibboleth.oidc.saml.xmlobject.JwksUri { +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLMarshaller.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLMarshaller.java new file mode 100644 index 000000000..5b390095f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLMarshaller.java @@ -0,0 +1,14 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import net.shibboleth.utilities.java.support.xml.ElementSupport; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.io.AbstractXMLObjectMarshaller; +import org.opensaml.core.xml.io.MarshallingException; +import org.w3c.dom.Element; + +public class ValueXMLMarshaller extends AbstractXMLObjectMarshaller { + protected void marshallElementContent(final XMLObject xmlObject, final Element domElement) throws MarshallingException { + final ValueXMLObject valueXMLObject = (ValueXMLObject) xmlObject; + ElementSupport.appendTextContent(domElement, valueXMLObject.getValue()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLObject.java new file mode 100644 index 000000000..eb00ea2e2 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLObject.java @@ -0,0 +1,9 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +/** + * ValueXMLObject is an XML Object that has a "value" through String getValue() and void setValue(String) methods + */ +public interface ValueXMLObject { + String getValue(); + void setValue(String value); +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLUnmarshaller.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLUnmarshaller.java new file mode 100644 index 000000000..386a1f003 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/oidc/ValueXMLUnmarshaller.java @@ -0,0 +1,11 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.io.AbstractXMLObjectUnmarshaller; + +public class ValueXMLUnmarshaller extends AbstractXMLObjectUnmarshaller { + protected void processElementContent(final XMLObject xmlObject, final String elementContent) { + final ValueXMLObject valueXMLObject = (ValueXMLObject) xmlObject; + valueXMLObject.setValue(elementContent); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java index 24757d560..b13078e56 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/opensaml/config/JPAXMLObjectProviderInitializer.java @@ -18,7 +18,8 @@ protected String[] getConfigResources() { "/jpa-schema-config.xml", "/jpa-signature-config.xml", "/saml2-protocol-config.xml", - "/modified-saml2-assertion-config.xml" + "/modified-saml2-assertion-config.xml", + "/jpa-shib-oidc-config.xml" }; } } \ No newline at end of file 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 291f659f8..bd14193e0 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 @@ -6,16 +6,19 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.IRelyingPartyOverrideProperty; import edu.internet2.tier.shibboleth.admin.ui.domain.KeyDescriptor; import edu.internet2.tier.shibboleth.admin.ui.domain.UIInfo; +import edu.internet2.tier.shibboleth.admin.ui.domain.X509Data; import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean; import edu.internet2.tier.shibboleth.admin.ui.domain.XSInteger; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.AssertionConsumerServiceRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ContactRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.KeyDescriptorRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.LogoutEndpointRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.ValueXMLObject; import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound; import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException; import edu.internet2.tier.shibboleth.admin.ui.exception.InvalidPatternMatchException; @@ -44,6 +47,10 @@ import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getStringListOfAttributeValues; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.xmlsec.signature.KeyInfo; +import org.opensaml.xmlsec.signature.KeyName; +import org.opensaml.xmlsec.signature.KeyValue; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -236,42 +243,8 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope } } - // set up security - // TODO: cleanup, probably use a lazy initializer - SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); - if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getWantAssertionsSigned() != null && ed.getSPSSODescriptor("").getWantAssertionsSigned()) { - if (securityInfoRepresentation == null) { - securityInfoRepresentation = new SecurityInfoRepresentation(); - representation.setSecurityInfo(securityInfoRepresentation); - } - securityInfoRepresentation.setWantAssertionsSigned(true); - } - if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").isAuthnRequestsSigned() != null && ed.getSPSSODescriptor("").isAuthnRequestsSigned()) { - if (securityInfoRepresentation == null) { - securityInfoRepresentation = new SecurityInfoRepresentation(); - representation.setSecurityInfo(securityInfoRepresentation); - } - securityInfoRepresentation.setAuthenticationRequestsSigned(true); - } - if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getKeyDescriptors().size() > 0) { - if (securityInfoRepresentation == null) { - securityInfoRepresentation = new SecurityInfoRepresentation(); - representation.setSecurityInfo(securityInfoRepresentation); - } - securityInfoRepresentation.setX509CertificateAvailable(true); - for (org.opensaml.saml.saml2.metadata.KeyDescriptor keyDescriptor : ed.getSPSSODescriptor("").getKeyDescriptors()) { - SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation = new SecurityInfoRepresentation.X509CertificateRepresentation(); - x509CertificateRepresentation.setName(((KeyDescriptor) keyDescriptor).getName()); - //TODO: check this. assume that if no value is set, it's used for both - if (keyDescriptor.getUse() != null) { - x509CertificateRepresentation.setType(keyDescriptor.getUse().toString().toLowerCase()); - } else { - x509CertificateRepresentation.setType("both"); - } - x509CertificateRepresentation.setValue(keyDescriptor.getKeyInfo().getX509Datas().get(0).getX509Certificates().get(0).getValue()); - securityInfoRepresentation.getX509Certificates().add(x509CertificateRepresentation); - } - } + // set up security - this block assumes too much like there will be a cert. With OIDC could not be some... + setupSecurityRepresentationFromEntityDescriptor(ed, representation); // set up ACSs if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getAssertionConsumerServices().size() > 0) { @@ -423,9 +396,94 @@ public Map getRelyingPartyOverridesRepresentationFromAttributeLi return ModelRepresentationConversions.getRelyingPartyOverridesRepresentationFromAttributeList(attributeList); } + private void setupSecurityRepresentationFromEntityDescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); + if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getWantAssertionsSigned() != null && ed.getSPSSODescriptor("").getWantAssertionsSigned()) { + if (securityInfoRepresentation == null) { + securityInfoRepresentation = new SecurityInfoRepresentation(); + representation.setSecurityInfo(securityInfoRepresentation); + } + securityInfoRepresentation.setWantAssertionsSigned(true); + } + if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").isAuthnRequestsSigned() != null && ed.getSPSSODescriptor("").isAuthnRequestsSigned()) { + if (securityInfoRepresentation == null) { + securityInfoRepresentation = new SecurityInfoRepresentation(); + representation.setSecurityInfo(securityInfoRepresentation); + } + securityInfoRepresentation.setAuthenticationRequestsSigned(true); + } + + // If the EntityDescriptor has key descriptors - parse them out. + if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getKeyDescriptors().size() > 0) { + if (securityInfoRepresentation == null) { + securityInfoRepresentation = new SecurityInfoRepresentation(); + representation.setSecurityInfo(securityInfoRepresentation); + } + + for (org.opensaml.saml.saml2.metadata.KeyDescriptor keyDescriptor : ed.getSPSSODescriptor("").getKeyDescriptors()) { + KeyDescriptorRepresentation keyDescriptorRep = new KeyDescriptorRepresentation(); + String name = keyDescriptor.getKeyInfo().getKeyNames().size() > 0 ? keyDescriptor.getKeyInfo().getKeyNames().get(0).getValue() : null; + keyDescriptorRep.setName(name); + + //TODO: check this. assume that if no value is set, it's used for both + String useType = keyDescriptor.getUse() != null ? keyDescriptor.getUse().toString().toLowerCase() : "both"; + keyDescriptorRep.setType(useType); + + KeyInfo keyInfo = keyDescriptor.getKeyInfo(); + KeyDescriptorRepresentation.ElementType keyInfoType = determineKeyInfoType(keyInfo); + keyDescriptorRep.setElementType(keyInfoType); + if (keyInfoType != KeyDescriptorRepresentation.ElementType.unsupported) { + List children = keyInfo.getOrderedChildren().stream().filter(xmlObj -> { + boolean xmlWeDoNotWant = xmlObj instanceof KeyName || xmlObj instanceof KeyValue; + return !xmlWeDoNotWant; + }).collect(Collectors.toList()); + XMLObject obj = children.get(0); + if (keyInfoType == KeyDescriptorRepresentation.ElementType.X509Data) { + obj = ((X509Data) obj).getX509Certificates().get(0); + } + keyDescriptorRep.setValue(((ValueXMLObject) obj).getValue()); + securityInfoRepresentation.addKeyDescriptor(keyDescriptorRep); + } + + // TODO remove this when done. + if (keyInfoType == KeyDescriptorRepresentation.ElementType.X509Data) { + SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation = new SecurityInfoRepresentation.X509CertificateRepresentation(); + x509CertificateRepresentation.setName(name); + x509CertificateRepresentation.setType(useType); + x509CertificateRepresentation.setValue(keyDescriptorRep.getValue()); + securityInfoRepresentation.getX509Certificates().add(x509CertificateRepresentation); + } + } + } + } + + private KeyDescriptorRepresentation.ElementType determineKeyInfoType(KeyInfo keyInfo) { + List children = keyInfo.getOrderedChildren().stream().filter(xmlObj -> { + boolean xmlWeDoNotWant = xmlObj instanceof KeyName || xmlObj instanceof KeyValue; + return !xmlWeDoNotWant; + }).collect(Collectors.toList()); + if (children.size() < 1) { + return KeyDescriptorRepresentation.ElementType.unsupported; + } + XMLObject xmlObject = children.get(0); + switch (xmlObject.getElementQName().getLocalPart()) { + case "X509Data": + return KeyDescriptorRepresentation.ElementType.X509Data; + case "ClientSecret": + return KeyDescriptorRepresentation.ElementType.clientSecret; + case "ClientSecretKeyReference": + return KeyDescriptorRepresentation.ElementType.clientSecretKeyReference; + case "JwksData": + return KeyDescriptorRepresentation.ElementType.jwksData; + case "JwksUri": + return KeyDescriptorRepresentation.ElementType.jwksUri; + default: + return KeyDescriptorRepresentation.ElementType.unsupported; + } + } + @Override - public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRep) - throws ForbiddenException, PersistentEntityNotFound, InvalidPatternMatchException { + public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRep) throws ForbiddenException, PersistentEntityNotFound, InvalidPatternMatchException { EntityDescriptor existingEd = entityDescriptorRepository.findByResourceId(edRep.getId()); if (existingEd == null) { throw new PersistentEntityNotFound(String.format("The entity descriptor with entity id [%s] was not found for update.", edRep.getId())); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java index 5fd444344..3034d5c7d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/EntityDescriptorConversionUtils.java @@ -241,7 +241,8 @@ public static void setupOrganization(EntityDescriptor ed, EntityDescriptorRepres ed.setOrganization(null); } } - + + // Building the EntityDescriptor from the representation public static void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation representation) { if (representation.getSecurityInfo() != null) { SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); @@ -263,13 +264,11 @@ public static void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresenta descriptor.setWantAssertionsSigned((Boolean)null); } } - // TODO: review if we need more than a naive implementation ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear()); - if (securityInfoRepresentation.isX509CertificateAvailable()) { - for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { - KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); - getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); - } + // TODO will need to fill in keydescriptors from generic list not the x509 list + for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { + KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); + getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); } } else { ed.getOptionalSPSSODescriptor().ifPresent( spssoDescriptor -> { diff --git a/backend/src/main/resources/db/changelog/changelog.sql b/backend/src/main/resources/db/changelog/changelog.sql index af15ca6b8..234aeda66 100644 --- a/backend/src/main/resources/db/changelog/changelog.sql +++ b/backend/src/main/resources/db/changelog/changelog.sql @@ -195,4 +195,32 @@ GO ALTER TABLE description ALTER COLUMN descriptionValue TYPE TEXT; GO ALTER TABLE description_aud ALTER COLUMN descriptionValue TYPE TEXT; +GO + +-- changeset liquibase:1.14.0.1 dbms:mariadb,mysql +-- preconditions onFail:MARK_RAN +-- precondition-sql-check expectedResult:1 SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'users' +-- comment: /* we don't need to run this if the system is new */ + +ALTER TABLE description ALTER COLUMN description_value LONGTEXT; +GO +ALTER TABLE description_aud ALTER COLUMN description_value LONGTEXT; +GO +ALTER TABLE description ALTER COLUMN text_context LONGTEXT; +GO +ALTER TABLE description_aud ALTER COLUMN text_context LONGTEXT; +GO + +-- changeset liquibase:1.14.0.2 dbms:postgresql,mssql +-- preconditions onFail:MARK_RAN +-- precondition-sql-check expectedResult:1 SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'users' +-- comment: /* we don't need to run this if the system is new */ + +ALTER TABLE xsany ALTER COLUMN text_context TYPE TEXT; +GO +ALTER TABLE xsany_aud ALTER COLUMN text_context TYPE TEXT; +GO +ALTER TABLE description ALTER COLUMN description_value TYPE TEXT; +GO +ALTER TABLE description_aud ALTER COLUMN description_value TYPE TEXT; GO \ No newline at end of file diff --git a/backend/src/main/resources/jpa-shib-oidc-config.xml b/backend/src/main/resources/jpa-shib-oidc-config.xml new file mode 100644 index 000000000..2b850a271 --- /dev/null +++ b/backend/src/main/resources/jpa-shib-oidc-config.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/src/main/resources/jpa-signature-config.xml b/backend/src/main/resources/jpa-signature-config.xml index 9a8da32e8..b2450ac71 100644 --- a/backend/src/main/resources/jpa-signature-config.xml +++ b/backend/src/main/resources/jpa-signature-config.xml @@ -16,16 +16,7 @@ - - - - + @@ -56,20 +47,20 @@ - + +--> + @@ -84,13 +75,13 @@ - + @@ -98,13 +89,13 @@ - + @@ -154,13 +145,13 @@ - + diff --git a/backend/src/main/templates/OIDCBuilderTemplate.java b/backend/src/main/templates/OIDCBuilderTemplate.java new file mode 100644 index 000000000..44e23e3a9 --- /dev/null +++ b/backend/src/main/templates/OIDCBuilderTemplate.java @@ -0,0 +1,21 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.oidc; + +import edu.internet2.tier.shibboleth.admin.ui.opensaml.xml.AbstractSAMLObjectBuilder; +import net.shibboleth.oidc.saml.xmlobject.Constants; + +public class {{TOKEN}}Builder extends AbstractSAMLObjectBuilder<{{TOKEN}}> { + public {{TOKEN}}Builder() { + } + + public {{TOKEN}} buildObject() { + return buildObject(Constants.SAML20MDOIDCMD_NS, {{TOKEN}}.DEFAULT_ELEMENT_LOCAL_NAME, Constants.SAML20MDOIDCMD_PREFIX); + } + + public {{TOKEN}} buildObject(final String namespaceURI, final String localName, final String namespacePrefix) { + {{TOKEN}} o = new {{TOKEN}}(); + o.setNamespaceURI(namespaceURI); + o.setElementLocalName(localName); + o.setNamespacePrefix(namespacePrefix); + return o; + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index e9a9aa217..004e873ab 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -302,7 +302,6 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { def test = openSamlObjects.marshalToXmlString(service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { it.entityId = 'http://test.example.org/test1' it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [new SecurityInfoRepresentation.X509CertificateRepresentation().with { it.type = 'signing' it.value = 'certificate' @@ -342,7 +341,6 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { def test = openSamlObjects.marshalToXmlString(service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { it.entityId = 'http://test.example.org/test1' it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [new SecurityInfoRepresentation.X509CertificateRepresentation().with { it.type = 'encryption' it.value = 'certificate' @@ -382,7 +380,6 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { def test = openSamlObjects.marshalToXmlString(service.createDescriptorFromRepresentation(new EntityDescriptorRepresentation().with { it.entityId = 'http://test.example.org/test1' it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [new SecurityInfoRepresentation.X509CertificateRepresentation().with { it.type = 'both' it.value = 'certificate' diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy index d25e9813c..ca2296299 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/EntityDescriptorConversionUtilsTests.groovy @@ -592,7 +592,6 @@ class EntityDescriptorConversionUtilsTests extends Specification { description: 'add signing certificate', representation: new EntityDescriptorRepresentation().with { it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [ new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'test', type: 'signing', value: 'test') ] @@ -617,7 +616,6 @@ class EntityDescriptorConversionUtilsTests extends Specification { description: 'add another certificate', representation: new EntityDescriptorRepresentation().with { it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [ new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'test', type: 'signing', value: 'test'), new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'test2', type: 'encryption', value: 'test2') @@ -651,7 +649,6 @@ class EntityDescriptorConversionUtilsTests extends Specification { description: 'remove a certificate', representation: new EntityDescriptorRepresentation().with { it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = true it.x509Certificates = [ new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'test2', type: 'encryption', value: 'test2') ] @@ -684,7 +681,6 @@ class EntityDescriptorConversionUtilsTests extends Specification { description: 'remove all certificates', representation: new EntityDescriptorRepresentation().with { it.securityInfo = new SecurityInfoRepresentation().with { - it.x509CertificateAvailable = false it } it diff --git a/backend/src/test/resources/json/SHIBUI-187.json b/backend/src/test/resources/json/SHIBUI-187.json index 05415592f..74c42bf34 100644 --- a/backend/src/test/resources/json/SHIBUI-187.json +++ b/backend/src/test/resources/json/SHIBUI-187.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": [] diff --git a/backend/src/test/resources/json/SHIBUI-211.json b/backend/src/test/resources/json/SHIBUI-211.json index 1bb678714..835860a9a 100644 --- a/backend/src/test/resources/json/SHIBUI-211.json +++ b/backend/src/test/resources/json/SHIBUI-211.json @@ -18,7 +18,6 @@ "logoWidth": 100 }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": [] diff --git a/backend/src/test/resources/json/SHIBUI-219-1.json b/backend/src/test/resources/json/SHIBUI-219-1.json index 4f1851975..6d5a32587 100644 --- a/backend/src/test/resources/json/SHIBUI-219-1.json +++ b/backend/src/test/resources/json/SHIBUI-219-1.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": [] diff --git a/backend/src/test/resources/json/SHIBUI-219-2.json b/backend/src/test/resources/json/SHIBUI-219-2.json index 31c2a0d6d..f2dd93df1 100644 --- a/backend/src/test/resources/json/SHIBUI-219-2.json +++ b/backend/src/test/resources/json/SHIBUI-219-2.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": true, "wantAssertionsSigned": false, "x509Certificates": [] diff --git a/backend/src/test/resources/json/SHIBUI-219-3.json b/backend/src/test/resources/json/SHIBUI-219-3.json index 551cf3718..ae2e6ba70 100644 --- a/backend/src/test/resources/json/SHIBUI-219-3.json +++ b/backend/src/test/resources/json/SHIBUI-219-3.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": [] diff --git a/backend/src/test/resources/json/SHIBUI-223.json b/backend/src/test/resources/json/SHIBUI-223.json index 50a0c1334..829a4c832 100644 --- a/backend/src/test/resources/json/SHIBUI-223.json +++ b/backend/src/test/resources/json/SHIBUI-223.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": true, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": [ diff --git a/backend/src/test/resources/json/SHIBUI-855.json b/backend/src/test/resources/json/SHIBUI-855.json index 14ff554b3..96207fafa 100644 --- a/backend/src/test/resources/json/SHIBUI-855.json +++ b/backend/src/test/resources/json/SHIBUI-855.json @@ -18,7 +18,6 @@ "logoWidth": null }, "securityInfo": { - "x509CertificateAvailable": false, "authenticationRequestsSigned": false, "wantAssertionsSigned": false, "x509Certificates": []