* @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE
* @link https://github.com/onelogin/php-saml
*/
namespace OneLogin\Saml2;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use DOMDocument;
use Exception;
/**
* Metadata lib of OneLogin PHP Toolkit
*/
class Metadata
{
const TIME_VALID = 172800; // 2 days
const TIME_CACHED = 604800; // 1 week
/**
* Generates the metadata of the SP based on the settings
*
* @param array $sp The SP data
* @param bool|string $authnsign authnRequestsSigned attribute
* @param bool|string $wsign wantAssertionsSigned attribute
* @param int|null $validUntil Metadata's valid time
* @param int|null $cacheDuration Duration of the cache in seconds
* @param array $contacts Contacts info
* @param array $organization Organization ingo
* @param array $attributes
*
* @return string SAML Metadata XML
*/
public static function builder($sp, $authnsign = false, $wsign = false, $validUntil = null, $cacheDuration = null, $contacts = array(), $organization = array(), $attributes = array())
{
if (!isset($validUntil)) {
$validUntil = time() + self::TIME_VALID;
}
$validUntilTime = Utils::parseTime2SAML($validUntil);
if (!isset($cacheDuration)) {
$cacheDuration = self::TIME_CACHED;
}
$sls = '';
if (isset($sp['singleLogoutService'])) {
$slsUrl = htmlspecialchars($sp['singleLogoutService']['url'], ENT_QUOTES);
$sls = <<
SLS_TEMPLATE;
}
if ($authnsign) {
$strAuthnsign = 'true';
} else {
$strAuthnsign = 'false';
}
if ($wsign) {
$strWsign = 'true';
} else {
$strWsign = 'false';
}
$strOrganization = '';
if (!empty($organization)) {
$organizationInfoNames = array();
$organizationInfoDisplaynames = array();
$organizationInfoUrls = array();
foreach ($organization as $lang => $info) {
$organizationInfoNames[] = <<{$info['name']}
ORGANIZATION_NAME;
$organizationInfoDisplaynames[] = <<{$info['displayname']}
ORGANIZATION_DISPLAY;
$organizationInfoUrls[] = <<{$info['url']}
ORGANIZATION_URL;
}
$orgData = implode("\n", $organizationInfoNames)."\n".implode("\n", $organizationInfoDisplaynames)."\n".implode("\n", $organizationInfoUrls);
$strOrganization = <<
{$orgData}
ORGANIZATIONSTR;
}
$strContacts = '';
if (!empty($contacts)) {
$contactsInfo = array();
foreach ($contacts as $type => $info) {
$contactsInfo[] = <<
{$info['givenName']}
{$info['emailAddress']}
CONTACT;
}
$strContacts = "\n".implode("\n", $contactsInfo);
}
$strAttributeConsumingService = '';
if (isset($sp['attributeConsumingService'])) {
$attrCsDesc = '';
if (isset($sp['attributeConsumingService']['serviceDescription'])) {
$attrCsDesc = sprintf(
' %s' . PHP_EOL,
$sp['attributeConsumingService']['serviceDescription']
);
}
if (!isset($sp['attributeConsumingService']['serviceName'])) {
$sp['attributeConsumingService']['serviceName'] = 'Service';
}
$requestedAttributeData = array();
foreach ($sp['attributeConsumingService']['requestedAttributes'] as $attribute) {
$requestedAttributeStr = sprintf(' ";
if (isset($attribute['attributeValue']) && !empty($attribute['attributeValue'])) {
$reqAttrAuxStr = '>';
if (is_string($attribute['attributeValue'])) {
$attribute['attributeValue'] = array($attribute['attributeValue']);
}
foreach ($attribute['attributeValue'] as $attrValue) {
$reqAttrAuxStr .=<<{$attrValue}
ATTRIBUTEVALUE;
}
$reqAttrAuxStr .= "\n ";
}
$requestedAttributeData[] = $requestedAttributeStr . $reqAttrAuxStr;
}
$requestedAttributeStr = implode(PHP_EOL, $requestedAttributeData);
$strAttributeConsumingService = <<
{$sp['attributeConsumingService']['serviceName']}
{$attrCsDesc}{$requestedAttributeStr}
METADATA_TEMPLATE;
}
$spEntityId = htmlspecialchars($sp['entityId'], ENT_QUOTES);
$acsUrl = htmlspecialchars($sp['assertionConsumerService']['url'], ENT_QUOTES);
$metadata = <<
{$sls} {$sp['NameIDFormat']}
{$strAttributeConsumingService}
{$strOrganization}{$strContacts}
METADATA_TEMPLATE;
return $metadata;
}
/**
* Signs the metadata with the key/cert provided
*
* @param string $metadata SAML Metadata XML
* @param string $key x509 key
* @param string $cert x509 cert
* @param string $signAlgorithm Signature algorithm method
* @param string $digestAlgorithm Digest algorithm method
*
* @return string Signed Metadata
*
* @throws Exception
*/
public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $digestAlgorithm = XMLSecurityDSig::SHA256)
{
return Utils::addSign($metadata, $key, $cert, $signAlgorithm, $digestAlgorithm);
}
/**
* Adds the x509 descriptors (sign/encryption) to the metadata
* The same cert will be used for sign/encrypt
*
* @param string $metadata SAML Metadata XML
* @param string $cert x509 cert
* @param bool $wantsEncrypted Whether to include the KeyDescriptor for encryption
*
* @return string Metadata with KeyDescriptors
*
* @throws Exception
*/
public static function addX509KeyDescriptors($metadata, $cert, $wantsEncrypted = true)
{
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
try {
$xml = Utils::loadXML($xml, $metadata);
if (!$xml) {
throw new Exception('Error parsing metadata');
}
} catch (Exception $e) {
throw new Exception('Error parsing metadata. '.$e->getMessage());
}
$formatedCert = Utils::formatCert($cert, false);
$x509Certificate = $xml->createElementNS(Constants::NS_DS, 'X509Certificate', $formatedCert);
$keyData = $xml->createElementNS(Constants::NS_DS, 'ds:X509Data');
$keyData->appendChild($x509Certificate);
$keyInfo = $xml->createElementNS(Constants::NS_DS, 'ds:KeyInfo');
$keyInfo->appendChild($keyData);
$keyDescriptor = $xml->createElementNS(Constants::NS_MD, "md:KeyDescriptor");
$SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0);
$SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
if ($wantsEncrypted === true) {
$SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
}
$signing = $xml->getElementsByTagName('KeyDescriptor')->item(0);
$signing->setAttribute('use', 'signing');
$signing->appendChild($keyInfo);
if ($wantsEncrypted === true) {
$encryption = $xml->getElementsByTagName('KeyDescriptor')->item(1);
$encryption->setAttribute('use', 'encryption');
$encryption->appendChild($keyInfo->cloneNode(true));
}
return $xml->saveXML();
}
}