diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php new file mode 100644 index 0000000000000000000000000000000000000000..b66d06b48eb87b35ec70abcf020530487c2a1985 --- /dev/null +++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php @@ -0,0 +1,286 @@ +<?php + +/** + * Class for generating SAML 2.0 metadata from simpleSAMLphp metadata arrays. + * + * This class builds SAML 2.0 metadata for an entity by examining the metadata for the entity. + * + * @package simpleSAMLphp + * @version $Id$ + */ +class SimpleSAML_Metadata_SAMLBuilder { + + /** + * The DOMDocument we are working in. + */ + private $document; + + + /** + * The EntityDescriptor we are building. + */ + private $entityDescriptor; + + + /** + * Initialize the builder. + * + * @param string $entityId The entity id of the entity. + */ + public function __construct($entityId) { + assert('is_string($entityId)'); + + $this->document = new DOMDocument(); + $this->entityDescriptor = $this->createElement('EntityDescriptor'); + + $this->entityDescriptor->setAttribute('entityID', $entityId); + } + + + /** + * Retrieve the EntityDescriptor. + * + * Retrieve the EntityDescriptor element which is generated for this entity. + * @return DOMElement The EntityDescriptor element for this entity. + */ + public function getEntityDescriptor() { + return $this->entityDescriptor; + } + + + /** + * Add metadata set for entity. + * + * This function is used to add a metadata array to the entity. + * + * @param string $set The metadata set this metadata comes from. + * @param array $metadata The metadata. + */ + public function addMetadata($set, $metadata) { + assert('is_string($set)'); + assert('is_array($metadata)'); + + switch ($set) { + case 'saml20-sp-remote': + $this->addMetadataSP20($metadata); + break; + case 'saml20-idp-remote': + $this->addMetadataIdP20($metadata); + break; + case 'shib13-sp-remote': + $this->addMetadataSP11($metadata); + break; + case 'shib13-idp-remote': + $this->addMetadataIdP11($metadata); + break; + default: + SimpleSAML_Logger::warning('Unable to generate metadata for unknown type \'' . $set . '\'.'); + } + } + + + /** + * Add SAML 2.0 SP metadata. + * + * @param array $metadata The metadata. + */ + public function addMetadataSP20($metadata) { + assert('is_array($metadata)'); + + $e = $this->createElement('SPSSODescriptor'); + $e->setAttribute('protocolSupportEnumeration', 'urn:oasis:names:tc:SAML:2.0:protocol'); + + $this->addCertificate($e, $metadata); + + if (array_key_exists('SingleLogoutService', $metadata)) { + $t = $this->createElement('SingleLogoutService'); + $t->setAttribute('Binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'); + $t->setAttribute('Location', $metadata['SingleLogoutService']); + + if (array_key_exists('SingleLogoutServiceResponse', $metadata)) { + $t->setAttribute('ResponseLocation', $metadata['SingleLogoutServiceResponse']); + } + + $e->appendChild($t); + } + + if (array_key_exists('NameIDFormat', $metadata)) { + $t = $this->createElement('NameIDFormat'); + $t->appendChild($this->document->createTextNode($metadata['NameIDFormat'])); + $e->appendChild($t); + } + + if (array_key_exists('AssertionConsumerService', $metadata)) { + $t = $this->createElement('AssertionConsumerService'); + $t->setAttribute('Binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'); + $t->setAttribute('Location', $metadata['AssertionConsumerService']); + $e->appendChild($t); + } + + $this->entityDescriptor->appendChild($e); + } + + + /** + * Add SAML 2.0 IdP metadata. + * + * @param array $metadata The metadata. + */ + public function addMetadataIdP20($metadata) { + assert('is_array($metadata)'); + + $e = $this->createElement('IDPSSODescriptor'); + $e->setAttribute('protocolSupportEnumeration', 'urn:oasis:names:tc:SAML:2.0:protocol'); + + $this->addCertificate($e, $metadata); + + if (array_key_exists('SingleLogoutService', $metadata)) { + $t = $this->createElement('SingleLogoutService'); + $t->setAttribute('Binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'); + $t->setAttribute('Location', $metadata['SingleLogoutService']); + + if (array_key_exists('SingleLogoutServiceResponse', $metadata)) { + $t->setAttribute('ResponseLocation', $metadata['SingleLogoutServiceResponse']); + } + + $e->appendChild($t); + } + + if (array_key_exists('NameIDFormat', $metadata)) { + $t = $this->createElement('NameIDFormat'); + $t->appendChild($this->document->createTextNode($metadata['NameIDFormat'])); + $e->appendChild($t); + } + + if (array_key_exists('SingleSignOnService', $metadata)) { + $t = $this->createElement('SingleSignOnService'); + $t->setAttribute('Binding', 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'); + $t->setAttribute('Location', $metadata['SingleSignOnService']); + $e->appendChild($t); + } + + $this->entityDescriptor->appendChild($e); + } + + + /** + * Add SAML 1.1 SP metadata. + * + * @param array $metadata The metadata. + */ + public function addMetadataSP11($metadata) { + assert('is_array($metadata)'); + + $e = $this->createElement('SPSSODescriptor'); + $e->setAttribute('protocolSupportEnumeration', 'urn:oasis:names:tc:SAML:1.1:protocol'); + + $this->addCertificate($e, $metadata); + + if (array_key_exists('NameIDFormat', $metadata)) { + $t = $this->createElement('NameIDFormat'); + $t->appendChild($this->document->createTextNode($metadata['NameIDFormat'])); + $e->appendChild($t); + } + + if (array_key_exists('AssertionConsumerService', $metadata)) { + $t = $this->createElement('AssertionConsumerService'); + $t->setAttribute('Binding', 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post'); + $t->setAttribute('Location', $metadata['AssertionConsumerService']); + $e->appendChild($t); + } + + $this->entityDescriptor->appendChild($e); + } + + + /** + * Add SAML 1.1 IdP metadata. + * + * @param array $metadata The metadata. + */ + public function addMetadataIdP11($metadata) { + assert('is_array($metadata)'); + + $e = $this->createElement('IDPSSODescriptor'); + $e->setAttribute('protocolSupportEnumeration', 'urn:oasis:names:tc:SAML:1.1:protocol'); + + $this->addCertificate($e, $metadata); + + if (array_key_exists('NameIDFormat', $metadata)) { + $t = $this->createElement('NameIDFormat'); + $t->appendChild($this->document->createTextNode($metadata['NameIDFormat'])); + $e->appendChild($t); + } + + if (array_key_exists('SingleSignOnService', $metadata)) { + $t = $this->createElement('SingleSignOnService'); + $t->setAttribute('Binding', 'urn:mace:shibboleth:1.0:profiles:AuthnRequest'); + $t->setAttribute('Location', $metadata['SingleSignOnService']); + $e->appendChild($t); + } + + $this->entityDescriptor->appendChild($e); + } + + + /** + * Create DOMElement in metadata namespace. + * + * Helper function for creating DOMElements with the metadata namespace. + * + * @param string $name The name of the DOMElement. + * @return DOMElement The new DOMElement. + */ + private function createElement($name) { + assert('is_string($name)'); + + return $this->document->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', $name); + } + + + /** + * Add certificate. + * + * Helper function for adding a certificate to the metadata. + * + * @param DOMElement $ssoDesc The IDPSSODescroptor or SPSSODecriptor the certificate + * should be added to. + * @param array $metadata The metadata for the entity. + */ + private function addCertificate(DOMElement $ssoDesc, $metadata) { + assert('is_array($metadata)'); + + if (!array_key_exists('certificate', $metadata)) { + /* No certificate to add. */ + return; + } + + $globalConfig = SimpleSAML_Configuration::getInstance(); + + $certFile = $globalConfig->getPathValue('certdir') . $metadata['certificate']; + if (!file_exists($certFile)) { + throw new Exception('Could not find certificate file: ' . $certFile); + } + + $certData = file_get_contents($certFile); + $certData = XMLSecurityDSig::get509XCert($certData, TRUE); + + + $keyDescriptor = $this->createElement('KeyDescriptor'); + $ssoDesc->appendChild($keyDescriptor); + + $keyInfo = $this->document->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'ds:KeyInfo'); + $keyDescriptor->appendChild($keyInfo); + + $x509Data = $this->document->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'ds:X509Data'); + $keyInfo->appendChild($x509Data); + + $x509Certificate = $this->document->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'ds:X509Certificate'); + $x509Data->appendChild($x509Certificate); + + $x509Certificate->appendChild($this->document->createTextNode($certData)); + } + +} + +?> \ No newline at end of file