Viewing file: SAMLBuilder.php (20.06 KB) -rwxrwxr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?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 EntityDescriptor we are building. */ private $entityDescriptor;
private $maxCache = NULL; private $maxDuration = NULL;
/** * Initialize the builder. * * @param string $entityId The entity id of the entity. */ public function __construct($entityId, $maxCache = NULL, $maxDuration = NULL) { assert('is_string($entityId)');
$this->maxCache = $maxCache; $this->maxDuration = $maxDuration;
$this->entityDescriptor = new SAML2_XML_md_EntityDescriptor(); $this->entityDescriptor->entityID = $entityId; }
private function setExpiration($metadata) {
if (array_key_exists('expire', $metadata)) { if ($metadata['expire'] - time() < $this->maxDuration) $this->maxDuration = $metadata['expire'] - time(); }
if ($this->maxCache !== NULL) $this->entityDescriptor->cacheDuration = 'PT' . $this->maxCache . 'S'; if ($this->maxDuration !== NULL) $this->entityDescriptor->validUntil = time() + $this->maxDuration; }
/** * Retrieve the EntityDescriptor. * * Retrieve the EntityDescriptor element which is generated for this entity. * @return DOMElement The EntityDescriptor element for this entity. */ public function getEntityDescriptor() {
$xml = $this->entityDescriptor->toXML(); $xml->ownerDocument->appendChild($xml);
return $xml; }
/** * Retrieve the EntityDescriptor as text. * * This function serializes this EntityDescriptor, and returns it as text. * * @param bool $formatted Whether the returned EntityDescriptor should be * formatted first. * @return string The serialized EntityDescriptor. */ public function getEntityDescriptorText($formatted = TRUE) { assert('is_bool($formatted)');
$xml = $this->getEntityDescriptor(); if ($formatted) { SimpleSAML_Utilities::formatDOMElement($xml); }
return $xml->ownerDocument->saveXML(); }
/** * @param SimpleSAML_Configuration $metadata Metadata. * @param $e Reference to the element where the Extensions element should be included. */ private function addExtensions(SimpleSAML_Configuration $metadata, SAML2_XML_md_RoleDescriptor $e) {
if ($metadata->hasValue('tags')) { $a = new SAML2_XML_saml_Attribute(); $a->Name = 'tags'; foreach ($metadata->getArray('tags') as $tag) { $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($tag); } $e->Extensions[] = $a; }
if ($metadata->hasValue('hint.cidr')) { $a = new SAML2_XML_saml_Attribute(); $a->Name = 'hint.cidr'; foreach ($metadata->getArray('hint.cidr') as $hint) { $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($hint); } $e->Extensions[] = $a; }
if ($metadata->hasValue('scope')) { foreach ($metadata->getArray('scope') as $scopetext) { $s = new SAML2_XML_shibmd_Scope(); $s->scope = $scopetext; $s->regexp = FALSE; $e->Extensions[] = $s; } }
if ($metadata->hasValue('EntityAttributes')) { $ea = new SAML2_XML_mdattr_EntityAttributes(); foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) { $a = new SAML2_XML_saml_Attribute(); $a->Name = $attributeName; $a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
// Attribute names that is not URI is prefixed as this: '{nameformat}name' if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) { $a->Name = $matches[2]; $nameFormat = $matches[1]; if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) { $a->NameFormat = $nameFormat; } } foreach ($attributeValues as $attributeValue) { $a->AttributeValue[] = new SAML2_XML_saml_AttributeValue($attributeValue); } $ea->children[] = $a; } $this->entityDescriptor->Extensions[] = $ea; }
if ($metadata->hasValue('UIInfo')) { $ui = new SAML2_XML_mdui_UIInfo(); foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) { switch ($uiName) { case 'DisplayName': $ui->DisplayName = $uiValues; break; case 'Description': $ui->Description = $uiValues; break; case 'InformationURL': $ui->InformationURL = $uiValues; break; case 'PrivacyStatementURL': $ui->PrivacyStatementURL = $uiValues; break; case 'Keywords': foreach ($uiValues as $lang => $keywords) { $uiItem = new SAML2_XML_mdui_Keywords(); $uiItem->lang = $lang; $uiItem->Keywords = $keywords; $ui->Keywords[] = $uiItem; } break; case 'Logo': foreach ($uiValues as $logo) { $uiItem = new SAML2_XML_mdui_Logo(); $uiItem->url = $logo['url']; $uiItem->width = $logo['width']; $uiItem->height = $logo['height']; if (isset($logo['lang'])) { $uiItem->lang = $logo['lang']; } $ui->Logo[] = $uiItem; } break; } } $e->Extensions[] = $ui; }
if ($metadata->hasValue('DiscoHints')) { $dh = new SAML2_XML_mdui_DiscoHints(); foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) { switch ($dhName) { case 'IPHint': $dh->IPHint = $dhValues; break; case 'DomainHint': $dh->DomainHint = $dhValues; break; case 'GeolocationHint': $dh->GeolocationHint = $dhValues; break; } } $e->Extensions[] = $dh; } }
/** * Add Organization element. * * This function adds an organization element to the metadata. * * @param array $orgName An array with the localized OrganizatioName. * @param array $orgDisplayName An array with the localized OrganizatioDisplayName. * @param array $orgURL An array with the localized OrganizatioURL. */ public function addOrganization(array $orgName, array $orgDisplayName, array $orgURL) {
$org = new SAML2_XML_md_Organization();
$org->OrganizationName = $orgName; $org->OrganizationDisplayName = $orgDisplayName; $org->OrganizationURL = $orgURL;
$this->entityDescriptor->Organization = $org; }
/** * Add organization element based on metadata array. * * @param array $metadata The metadata we should extract the organization information from. */ public function addOrganizationInfo(array $metadata) {
if ( empty($metadata['OrganizationName']) || empty($metadata['OrganizationDisplayName']) || empty($metadata['OrganizationURL']) ) { /* Empty or incomplete organization information. */ return; }
$orgName = SimpleSAML_Utilities::arrayize($metadata['OrganizationName'], 'en'); $orgDisplayName = SimpleSAML_Utilities::arrayize($metadata['OrganizationDisplayName'], 'en'); $orgURL = SimpleSAML_Utilities::arrayize($metadata['OrganizationURL'], 'en');
$this->addOrganization($orgName, $orgDisplayName, $orgURL); }
/** * Add endpoint list to metadata. * * @param array $endpoints The endpoints. * @param bool $indexed Whether the endpoints should be indexed. * @return array Array of endpoint objects. */ private static function createEndpoints(array $endpoints, $indexed) { assert('is_bool($indexed)');
$ret = array();
foreach ($endpoints as &$ep) { if ($indexed) { $t = new SAML2_XML_md_IndexedEndpointType(); } else { $t = new SAML2_XML_md_EndpointType(); }
$t->Binding = $ep['Binding']; $t->Location = $ep['Location']; if (isset($ep['ResponseLocation'])) { $t->ResponseLocation = $ep['ResponseLocation']; } if (isset($ep['hoksso:ProtocolBinding'])) { $t->setAttributeNS(SAML2_Const::NS_HOK, 'hoksso:ProtocolBinding', SAML2_Const::BINDING_HTTP_REDIRECT); }
if ($indexed) { if (!isset($ep['index'])) { /* Find the maximum index. */ $maxIndex = -1; foreach ($endpoints as $ep) { if (!isset($ep['index'])) { continue; }
if ($ep['index'] > $maxIndex) { $maxIndex = $ep['index']; } }
$ep['index'] = $maxIndex + 1; }
$t->index = $ep['index']; }
$ret[] = $t; }
return $ret; }
/** * Add an AttributeConsumingService element to the metadata. * * @param DOMElement $spDesc The SPSSODescriptor element. * @param SimpleSAML_Configuration $metadata The metadata. */ private function addAttributeConsumingService(SAML2_XML_md_SPSSODescriptor $spDesc, SimpleSAML_Configuration $metadata) { $attributes = $metadata->getArray('attributes', array()); $name = $metadata->getLocalizedString('name', NULL);
if ($name === NULL || count($attributes) == 0) { /* We cannot add an AttributeConsumingService without name and attributes. */ return; }
$attributesrequired = $metadata->getArray('attributes.required', array());
/* * Add an AttributeConsumingService element with information as name and description and list * of requested attributes */ $attributeconsumer = new SAML2_XML_md_AttributeConsumingService();
$attributeconsumer->index = 0;
$attributeconsumer->ServiceName = $name; $attributeconsumer->ServiceDescription = $metadata->getLocalizedString('description', array());
$nameFormat = $metadata->getString('attributes.NameFormat', SAML2_Const::NAMEFORMAT_UNSPECIFIED); foreach ($attributes as $attribute) { $t = new SAML2_XML_md_RequestedAttribute(); $t->Name = $attribute; if ($nameFormat !== SAML2_Const::NAMEFORMAT_UNSPECIFIED) { $t->NameFormat = $nameFormat; } if (in_array($attribute, $attributesrequired)) { $t->isRequired = true; } $attributeconsumer->RequestedAttribute[] = $t; }
$spDesc->AttributeConsumingService[] = $attributeconsumer; }
/** * 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)'); $this->setExpiration($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; case 'attributeauthority-remote': $this->addAttributeAuthority($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. * @param array $protocols The protocols supported. */ public function addMetadataSP20($metadata, $protocols = array(SAML2_Const::NS_SAMLP)) { assert('is_array($metadata)'); assert('is_array($protocols)'); assert('isset($metadata["entityid"])'); assert('isset($metadata["metadata-set"])');
$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
$e = new SAML2_XML_md_SPSSODescriptor(); $e->protocolSupportEnumeration = $protocols;
$this->addExtensions($metadata, $e);
$this->addCertificate($e, $metadata);
$e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
$endpoints = $metadata->getEndpoints('AssertionConsumerService'); foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) { $endpoints[] = array( 'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact', 'Location' => $acs, ); } $e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
$this->addAttributeConsumingService($e, $metadata);
$this->entityDescriptor->RoleDescriptor[] = $e;
foreach ($metadata->getArray('contacts', array()) as $contact) { if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) { $this->addContact($contact['contactType'], $contact); } }
}
/** * Add SAML 2.0 IdP metadata. * * @param array $metadata The metadata. */ public function addMetadataIdP20($metadata) { assert('is_array($metadata)'); assert('isset($metadata["entityid"])'); assert('isset($metadata["metadata-set"])');
$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
$e = new SAML2_XML_md_IDPSSODescriptor(); $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:2.0:protocol';
if ($metadata->getBoolean('redirect.sign', FALSE)) { $e->WantAuthnRequestSigned = TRUE; }
$this->addExtensions($metadata, $e);
$this->addCertificate($e, $metadata);
if ($metadata->hasValue('ArtifactResolutionService')){ $e->ArtifactResolutionService = self::createEndpoints($metadata->getEndpoints('ArtifactResolutionService'), TRUE); }
$e->SingleLogoutService = self::createEndpoints($metadata->getEndpoints('SingleLogoutService'), FALSE);
$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
$e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
$this->entityDescriptor->RoleDescriptor[] = $e;
foreach ($metadata->getArray('contacts', array()) as $contact) { if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) { $this->addContact($contact['contactType'], $contact); } }
}
/** * Add SAML 1.1 SP metadata. * * @param array $metadata The metadata. */ public function addMetadataSP11($metadata) { assert('is_array($metadata)'); assert('isset($metadata["entityid"])'); assert('isset($metadata["metadata-set"])');
$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
$e = new SAML2_XML_md_SPSSODescriptor(); $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol';
$this->addCertificate($e, $metadata);
$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
$endpoints = $metadata->getEndpoints('AssertionConsumerService'); foreach ($metadata->getArrayizeString('AssertionConsumerService.artifact', array()) as $acs) { $endpoints[] = array( 'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01', 'Location' => $acs, ); } $e->AssertionConsumerService = self::createEndpoints($endpoints, TRUE);
$this->addAttributeConsumingService($e, $metadata);
$this->entityDescriptor->RoleDescriptor[] = $e; }
/** * Add SAML 1.1 IdP metadata. * * @param array $metadata The metadata. */ public function addMetadataIdP11($metadata) { assert('is_array($metadata)'); assert('isset($metadata["entityid"])'); assert('isset($metadata["metadata-set"])');
$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
$e = new SAML2_XML_md_IDPSSODescriptor(); $e->protocolSupportEnumeration[] = 'urn:oasis:names:tc:SAML:1.1:protocol'; $e->protocolSupportEnumeration[] = 'urn:mace:shibboleth:1.0';
$this->addCertificate($e, $metadata);
$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
$e->SingleSignOnService = self::createEndpoints($metadata->getEndpoints('SingleSignOnService'), FALSE);
$this->entityDescriptor->RoleDescriptor[] = $e; }
/** * Add a AttributeAuthorityDescriptor. * * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by SAMLParser. */ public function addAttributeAuthority(array $metadata) { assert('is_array($metadata)'); assert('isset($metadata["entityid"])'); assert('isset($metadata["metadata-set"])');
$metadata = SimpleSAML_Configuration::loadFromArray($metadata, $metadata['entityid']);
$e = new SAML2_XML_md_AttributeAuthorityDescriptor(); $e->protocolSupportEnumeration = $metadata->getArray('protocols', array());
$this->addExtensions($metadata, $e); $this->addCertificate($e, $metadata);
$e->AttributeService = self::createEndpoints($metadata->getEndpoints('AttributeService'), FALSE); $e->AssertionIDRequestService = self::createEndpoints($metadata->getEndpoints('AssertionIDRequestService'), FALSE);
$e->NameIDFormat = $metadata->getArrayizeString('NameIDFormat', array());
$this->entityDescriptor->RoleDescriptor[] = $e; }
/** * Add contact information. * * Accepts a contact type, and an array of the following elements (all are optional): * - emailAddress Email address (as string), or array of email addresses. * - telephoneNumber Telephone number of contact (as string), or array of telephone numbers. * - name Full name of contact, either as <GivenName> <SurName>, or as <SurName>, <GivenName>. * - surName Surname of contact. * - givenName Givenname of contact. * - company Company name of contact. * * 'name' will only be used if neither givenName nor surName is present. * * The following contact types are allowed: * "technical", "support", "administrative", "billing", "other" * * @param string $type The type of contact. * @param array $details The details about the contact. */ public function addContact($type, $details) { assert('is_string($type)'); assert('is_array($details)'); assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
/* Parse name into givenName and surName. */ if (isset($details['name']) && empty($details['surName']) && empty($details['givenName'])) { $names = explode(',', $details['name'], 2); if (count($names) === 2) { $details['surName'] = trim($names[0]); $details['givenName'] = trim($names[1]); } else { $names = explode(' ', $details['name'], 2); if (count($names) === 2) { $details['givenName'] = trim($names[0]); $details['surName'] = trim($names[1]); } else { $details['surName'] = trim($names[0]); } } }
$e = new SAML2_XML_md_ContactPerson(); $e->contactType = $type;
if (isset($details['company'])) { $e->Company = $details['company']; } if (isset($details['givenName'])) { $e->GivenName = $details['givenName']; } if (isset($details['surName'])) { $e->SurName = $details['surName']; }
if (isset($details['emailAddress'])) { $eas = $details['emailAddress']; if (!is_array($eas)) { $eas = array($eas); } foreach ($eas as $ea) { $e->EmailAddress[] = $ea; } }
if (isset($details['telephoneNumber'])) { $tlfNrs = $details['telephoneNumber']; if (!is_array($tlfNrs)) { $tlfNrs = array($tlfNrs); } foreach ($tlfNrs as $tlfNr) { $e->TelephoneNumber[] = $tlfNr; } }
$this->entityDescriptor->ContactPerson[] = $e; }
/** * Add a KeyDescriptor with an X509 certificate. * * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to. * @param string $use The value of the use-attribute. * @param string $x509data The certificate data. */ private function addX509KeyDescriptor(SAML2_XML_md_RoleDescriptor $rd, $use, $x509data) { assert('in_array($use, array("encryption", "signing"), TRUE)'); assert('is_string($x509data)');
$keyDescriptor = SAML2_Utils::createKeyDescriptor($x509data); $keyDescriptor->use = $use; $rd->KeyDescriptor[] = $keyDescriptor; }
/** * Add certificate. * * Helper function for adding a certificate to the metadata. * * @param SAML2_XML_md_RoleDescriptor $rd The RoleDescriptor the certificate should be added to. * @param SimpleSAML_Configuration $metadata The metadata for the entity. */ private function addCertificate(SAML2_XML_md_RoleDescriptor $rd, SimpleSAML_Configuration $metadata) {
$keys = $metadata->getPublicKeys(); if ($keys !== NULL) { foreach ($keys as $key) { if ($key['type'] !== 'X509Certificate') { continue; } if (!isset($key['signing']) || $key['signing'] === TRUE) { $this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate']); } if (!isset($key['encryption']) || $key['encryption'] === TRUE) { $this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate']); } } }
if ($metadata->hasValue('https.certData')) { $this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData')); } }
}
|