Skip to content
Snippets Groups Projects
Commit 7afde767 authored by Olav Morken's avatar Olav Morken
Browse files

Add support for the MDUI extension.

This patch adds support parsing and generating metadata with the
mdui:UIInfo and mdui:DiscoHints elements.

Support for generating metadata with the extensions is added to the
SAML 2.0 IdP. It should also work through the metadata aggregator.

Thanks to Timothy Ace at Synacor, Inc. for implementing this!

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@3088 44740490-163a-0410-bde0-09ae8108e29a
parent e651270b
Branches
Tags
No related merge requests found
SAML V2.0 Metadata Extensions for Login and Discovery User Interface
=============================
<!--
This file is written in Markdown syntax.
For more information about how to use the Markdown syntax, read here:
http://daringfireball.net/projects/markdown/syntax
-->
* Version: `$Id:$`
* Author: Timothy Ace [tace@synacor.com](mailto:tace@synacor.com)
<!-- {{TOC}} -->
This is a reference for the SimpleSAMLphp implemenation of the [SAML
V2.0 Attribute Extensions](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-attribute-ext.pdf)
defined by OASIS.
The `metadata/saml20-idp-hosted.php` entries are used to define the
metadata extension items. An example of this is:
<?php
$metadata['entity-id-1'] = array(
/* ... */
'EntityAttributes' => array(
'urn:simplesamlphp:v1:simplesamlphp' => array('is', 'really', 'cool'),
'{urn:simplesamlphp:v1}foo' => array('bar'),
),
/* ... */
);
The OASIS specification primarily defines how to include arbitrary
`Attribute` and `Assertion` elements within the metadata for an IdP.
*Note*: SimpleSAMLphp does not support `Assertion` elements within the
metadata at this time.
Defining Attributes
--------------
The `EntityAttributes` key is used to define the attributes in the
metadata. Each item in the `EntityAttributes` array defines a new
`<Attribute>` item in the metadata. The value for each key must be an
array. Each item in this array produces a separte `<AttributeValue>`
element within the `<Attribute>` element.
'EntityAttributes' => array(
'urn:simplesamlphp:v1:simplesamlphp' => array('is', 'really', 'cool'),
),
This generates:
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="urn:simplesamlphp:v1:simplesamlphp" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">is</saml:AttributeValue>
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">really</saml:AttributeValue>
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">cool</saml:AttributeValue>
</saml:Attribute>
Each `<Attribute>` element requires a `NameFormat` attribute. This is
specified using curly braces at the beginning of the key name:
'EntityAttributes' => array(
'{urn:simplesamlphp:v1}foo' => array('bar'),
),
This generates:
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="foo" NameFormat="urn:simplesamlphp:v1">
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">bar</saml:AttributeValue>
</saml:Attribute>
When the curly braces are omitted, the NameFormat is automatically set
to "urn:oasis:names:tc:SAML:2.0:attrname-format:uri".
Generated XML Metadata Examples
----------------
If given the following configuration...
$metadata['https://www.example.com/saml/saml2/idp/metadata.php'] = array(
'host' => 'www.example.com',
'certificate' => 'server.crt',
'privatekey' => 'server.pem',
'auth' => 'example-userpass',
'EntityAttributes' => array(
'urn:simplesamlphp:v1:simplesamlphp' => array('is', 'really', 'cool'),
'{urn:simplesamlphp:v1}foo' => array('bar'),
),
);
... will generate the following XML metadata:
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://www.example.com/saml/saml2/idp/metadata.php">
<md:Extensions>
<mdattr:EntityAttributes xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute">
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="urn:simplesamlphp:v1:simplesamlphp" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">is</saml:AttributeValue>
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">really</saml:AttributeValue>
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">cool</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="foo" NameFormat="urn:simplesamlphp:v1">
<saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">bar</saml:AttributeValue>
</saml:Attribute>
</mdattr:EntityAttributes>
</md:Extensions>
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
...
SAML V2.0 Metadata Extensions for Login and Discovery User Interface
=============================
<!--
This file is written in Markdown syntax.
For more information about how to use the Markdown syntax, read here:
http://daringfireball.net/projects/markdown/syntax
-->
* Version: `$Id:$`
* Author: Timothy Ace [tace@synacor.com](mailto:tace@synacor.com)
<!-- {{TOC}} -->
This is a reference for the SimpleSAMLphp implemenation of the [SAML
V2.0 Metadata Extensions for Login and Discovery User Interface](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf)
defined by OASIS.
The `metadata/saml20-idp-hosted.php` entries are used to define the
metadata extension items. An example of this is:
<?php
$metadata['entity-id-1'] = array(
/* ... */
'UIInfo' => array(
'DisplayName' => array(
'en' => 'English name',
'es' => 'Nombre en Español',
),
'Description' => array(
'en' => 'English description',
'es' => 'Descripción en Español',
),
'InformationURL' => array(
'en' => 'http://example.com/info/en',
'es' => 'http://example.com/info/es',
),
'PrivacyStatementURL' => array(
'en' => 'http://example.com/privacy/en',
'es' => 'http://example.com/privacy/es',
),
'Keywords' => array(
'en' => array('communication', 'federated session'),
'es' => array('comunicación', 'sesión federated'),
),
'Logo' => array(
array(
'url' => 'http://example.com/logo1.png',
'height' => 200,
'width' => 400,
'lang' => 'en',
),
array(
'url' => 'http://example.com/logo2.png',
'height' => 201,
'width' => 401,
),
),
),
'DiscoHints' => array(
'IPHint' => array('130.59.0.0/16', '2001:620::0/96'),
'DomainHint' => array('example.com', 'www.example.com'),
'GeolocationHint' => array('geo:47.37328,8.531126', 'geo:19.34343,12.342514'),
),
/* ... */
);
The OASIS specification primarily defines how an IdP can communicate
metadata related to IdP discovery. There are two different types of
extensions defined. There are the `<mdui:UIInfo>`elements that define
how an IdP should be displayed and there are the `<mdui:DiscoHints>`
elements that define when an IdP should be choosen/displayed.
UIInfo Items
--------------
These elements are used for IdP discovery to determine what to display
about an IdP. These properties are all children of the `UIInfo` key.
*Note*: Most elements are localized strings that specify the language
using the array key as the language-code:
'DisplayName' => array(
'en' => 'English name',
'es' => 'Nombre en Español',
),
`DisplayName`
: The localized list of names for this IdP
'DisplayName' => array(
'en' => 'English name',
'es' => 'Nombre en Español',
),
`Description`
: The localized list of statements used to decribe this IdP
'Description' => array(
'en' => 'English description',
'es' => 'Descripción en Español',
),
`InformationURL`
: A localized list of URLs where more information about the IdP is
located.
'InformationURL' => array(
'en' => 'http://example.com/info/en',
'es' => 'http://example.com/info/es',
),
`PrivacyStatementURL`
: A localized list of URLs where the IdP's privacy statement is
located.
'PrivacyStatementURL' => array(
'en' => 'http://example.com/privacy/en',
'es' => 'http://example.com/privacy/es',
),
`Keywords`
: A localized list of keywords used to describe the IdP
'Keywords' => array(
'en' => array('communication', 'federated session'),
'es' => array('comunicación', 'sesión federated'),
),
: *Note*: The `+` (plus) character is forbidden by specification from
being part of a Keyword.
`Logo`
: The logos used to represent the IdP
'Logo' => array(
array(
'url' => 'http://example.com/logo1.png',
'height' => 200,
'width' => 400,
'lang' => 'en',
),
array(
'url' => 'http://example.com/logo2.png',
'height' => 201,
'width' => 401,
),
),
: An optional `lang` key containing a language-code is supported for
localized Logos.
DiscoHints Items
--------------
These elements are used for IdP discovery to determine when to choose or
present an IdP. These properties are all children of the `DiscoHints`
key.
`IPHint`
: This is a list of both IPv4 and IPv6 addresses in CIDR notation
services by or associated with this entity.
'IPHint' => array('130.59.0.0/16', '2001:620::0/96'),
`DomainHint`
: This specifies a list of domain names serviced by or associated with
this entity.
'DomainHint' => array('example.com', 'www.example.com'),
`GeolocationHint`
: This specifies a list of geographic coordinates associated with, or
serviced by, the entity. Coordinates are given in URI form using the
geo URI scheme [RFC5870](http://www.ietf.org/rfc/rfc5870.txt).
'GeolocationHint' => array('geo:47.37328,8.531126', 'geo:19.34343,12.342514'),
Generated XML Metadata Examples
----------------
If given the following configuration...
$metadata['https://www.example.com/saml/saml2/idp/metadata.php'] = array(
'host' => 'www.example.com',
'certificate' => 'server.crt',
'privatekey' => 'server.pem',
'auth' => 'example-userpass',
'UIInfo' => array(
'DisplayName' => array(
'en' => 'English name',
'es' => 'Nombre en Español',
),
'Description' => array(
'en' => 'English description',
'es' => 'Descripción en Español',
),
'InformationURL' => array(
'en' => 'http://example.com/info/en',
'es' => 'http://example.com/info/es',
),
'PrivacyStatementURL' => array(
'en' => 'http://example.com/privacy/en',
'es' => 'http://example.com/privacy/es',
),
'Keywords' => array(
'en' => array('communication', 'federated session'),
'es' => array('comunicación', 'sesión federated'),
),
'Logo' => array(
array(
'url' => 'http://example.com/logo1.png',
'height' => 200,
'width' => 400,
),
array(
'url' => 'http://example.com/logo2.png',
'height' => 201,
'width' => 401,
),
),
),
'DiscoHints' => array(
'IPHint' => array('130.59.0.0/16', '2001:620::0/96'),
'DomainHint' => array('example.com', 'www.example.com'),
'GeolocationHint' => array('geo:47.37328,8.531126', 'geo:19.34343,12.342514'),
),
);
... will generate the following XML metadata:
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://www.example.com/saml/saml2/idp/metadata.php">
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:Extensions>
<mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
<mdui:DisplayName xml:lang="en">English name</mdui:DisplayName>
<mdui:DisplayName xml:lang="es">Nombre en Espa&#xF1;ol</mdui:DisplayName>
<mdui:Description xml:lang="en">English description</mdui:Description>
<mdui:Description xml:lang="es">Descripci&#xF3;n en Espa&#xF1;ol</mdui:Description>
<mdui:InformationURL xml:lang="en">http://example.com/info/en</mdui:InformationURL>
<mdui:InformationURL xml:lang="es">http://example.com/info/es</mdui:InformationURL>
<mdui:PrivacyStatementURL xml:lang="en">http://example.com/privacy/en</mdui:PrivacyStatementURL>
<mdui:PrivacyStatementURL xml:lang="es">http://example.com/privacy/es</mdui:PrivacyStatementURL>
<mdui:Keywords xml:lang="en">communication federated+session</mdui:Keywords>
<mdui:Keywords xml:lang="es">comunicaci&#xF3;n sesi&#xF3;n+federated</mdui:Keywords>
<mdui:Logo width="400" height="200" xml:lang="en">http://example.com/logo1.png</mdui:Logo>
<mdui:Logo width="401" height="201">http://example.com/logo2.png</mdui:Logo>
</mdui:UIInfo>
<mdui:DiscoHints xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
<mdui:IPHint>130.59.0.0/16</mdui:IPHint>
<mdui:IPHint>2001:620::0/96</mdui:IPHint>
<mdui:DomainHint>example.com</mdui:DomainHint>
<mdui:DomainHint>www.example.com</mdui:DomainHint>
<mdui:GeolocationHint>geo:47.37328,8.531126</mdui:GeolocationHint>
<mdui:GeolocationHint>geo:19.34343,12.342514</mdui:GeolocationHint>
</mdui:DiscoHints>
</md:Extensions>
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
...
...@@ -24,6 +24,10 @@ class SAML2_XML_md_Extensions { ...@@ -24,6 +24,10 @@ class SAML2_XML_md_Extensions {
$ret[] = new SAML2_XML_mdattr_EntityAttributes($node); $ret[] = new SAML2_XML_mdattr_EntityAttributes($node);
} elseif ($node->namespaceURI === SAML2_XML_mdrpi_Common::NS_MDRPI && $node->localName === 'PublicationInfo') { } elseif ($node->namespaceURI === SAML2_XML_mdrpi_Common::NS_MDRPI && $node->localName === 'PublicationInfo') {
$ret[] = new SAML2_XML_mdrpi_PublicationInfo($node); $ret[] = new SAML2_XML_mdrpi_PublicationInfo($node);
} elseif ($node->namespaceURI === SAML2_XML_mdui_UIInfo::NS && $node->localName === 'UIInfo') {
$ret[] = new SAML2_XML_mdui_UIInfo($node);
} elseif ($node->namespaceURI === SAML2_XML_mdui_DiscoHints::NS && $node->localName === 'DiscoHints') {
$ret[] = new SAML2_XML_mdui_DiscoHints($node);
} else { } else {
$ret[] = new SAML2_XML_Chunk($node); $ret[] = new SAML2_XML_Chunk($node);
} }
......
<?php
/**
* Class for handling the metadata extensions for login and discovery user interface
*
* @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
* @package simpleSAMLphp
* @version $Id$
*/
class SAML2_XML_mdui_DiscoHints {
/**
* The namespace used for the DiscoHints extension.
*/
const NS = 'urn:oasis:names:tc:SAML:metadata:ui';
/**
* Array with child elements.
*
* The elements can be any of the other SAML2_XML_mdui_* elements.
*
* @var array
*/
public $children = array();
/**
* The IPHint, as an array of strings.
*
* @var array
*/
public $IPHint = array();
/**
* The DomainHint, as an array of strings.
*
* @var array
*/
public $DomainHint = array();
/**
* The GeolocationHint, as an array of strings.
*
* @var array
*/
public $GeolocationHint = array();
/**
* Create a DiscoHints element.
*
* @param DOMElement|NULL $xml The XML element we should load.
*/
public function __construct(DOMElement $xml = NULL) {
if ($xml === NULL) {
return;
}
$this->IPHint = SAML2_Utils::extractStrings($xml, self::NS, 'IPHint');
$this->DomainHint = SAML2_Utils::extractStrings($xml, self::NS, 'DomainHint');
$this->GeolocationHint = SAML2_Utils::extractStrings($xml, self::NS, 'GeolocationHint');
foreach (SAML2_Utils::xpQuery($xml, "./*[namespace-uri()!='".self::NS."']") as $node) {
$this->children[] = new SAML2_XML_Chunk($node);
}
}
/**
* Convert this DiscoHints to XML.
*
* @param DOMElement $parent The element we should append to.
*/
public function toXML(DOMElement $parent) {
assert('is_array($this->IPHint)');
assert('is_array($this->DomainHint)');
assert('is_array($this->GeolocationHint)');
assert('is_array($this->children)');
if (!empty($this->IPHint)
|| !empty($this->DomainHint)
|| !empty($this->GeolocationHint)
|| !empty($this->children)) {
$doc = $parent->ownerDocument;
$e = $doc->createElementNS(self::NS, 'mdui:DiscoHints');
$parent->appendChild($e);
if (!empty($this->children)) {
foreach ($this->children as $child) {
$child->toXML($e);
}
}
SAML2_Utils::addStrings($e, self::NS, 'mdui:IPHint', FALSE, $this->IPHint);
SAML2_Utils::addStrings($e, self::NS, 'mdui:DomainHint', FALSE, $this->DomainHint);
SAML2_Utils::addStrings($e, self::NS, 'mdui:GeolocationHint', FALSE, $this->GeolocationHint);
return $e;
}
}
}
<?php
/**
* Class for handling the Keywords metadata extensions for login and discovery user interface
*
* @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
* @package simpleSAMLphp
* @version $Id$
*/
class SAML2_XML_mdui_Keywords {
/**
* The keywords of this item.
*
* @var string
*/
public $Keywords;
/**
* The language of this item.
*
* @var string
*/
public $lang;
/**
* Initialize a Keywords.
*
* @param DOMElement|NULL $xml The XML element we should load.
*/
public function __construct(DOMElement $xml = NULL) {
if ($xml === NULL) {
return;
}
if (!$xml->hasAttribute('xml:lang')) {
throw new Exception('Missing lang on Keywords.');
}
if (!is_string($xml->textContent) || !strlen($xml->textContent)) {
throw new Exception('Missing value for Keywords.');
}
$this->Keywords = array();
foreach (explode(' ', $xml->textContent) as $keyword) {
$this->Keywords[] = str_replace('+', ' ', $keyword);
}
$this->lang = $xml->getAttribute('xml:lang');
}
/**
* Convert this Keywords to XML.
*
* @param DOMElement $parent The element we should append this Keywords to.
*/
public function toXML(DOMElement $parent) {
assert('is_string($this->lang)');
assert('is_array($this->Keywords)');
$doc = $parent->ownerDocument;
$e = $doc->createElementNS(SAML2_XML_mdui_UIInfo::NS, 'mdui:Keywords');
$e->setAttribute('xml:lang', $this->lang);
$e->nodeValue = '';
foreach ($this->Keywords as $keyword) {
if (strpos($keyword, "+") !== false) {
throw new Exception('Keywords may not contain a "+" character.');
}
$e->nodeValue .= str_replace(' ', '+', $keyword) . ' ';
}
$e->nodeValue = rtrim($e->nodeValue);
$parent->appendChild($e);
return $e;
}
}
<?php
/**
* Class for handling the Logo metadata extensions for login and discovery user interface
*
* @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
* @package simpleSAMLphp
* @version $Id$
*/
class SAML2_XML_mdui_Logo {
/**
* The url of this logo.
*
* @var string
*/
public $url;
/**
* The width of this logo.
*
* @var string
*/
public $width;
/**
* The height of this logo.
*
* @var string
*/
public $height;
/**
* The language of this item.
*
* @var string
*/
public $lang;
/**
* Initialize a Logo.
*
* @param DOMElement|NULL $xml The XML element we should load.
*/
public function __construct(DOMElement $xml = NULL) {
if ($xml === NULL) {
return;
}
if (!$xml->hasAttribute('width')) {
throw new Exception('Missing width of Logo.');
}
if (!$xml->hasAttribute('height')) {
throw new Exception('Missing height of Logo.');
}
if (!is_string($xml->textContent) || !strlen($xml->textContent)) {
throw new Exception('Missing url value for Logo.');
}
$this->url = $xml->textContent;
$this->width = (int)$xml->getAttribute('width');
$this->height = (int)$xml->getAttribute('height');
$this->lang = $xml->hasAttribute('xml:lang') ? $xml->getAttribute('xml:lang') : NULL;
}
/**
* Convert this Logo to XML.
*
* @param DOMElement $parent The element we should append this Logo to.
*/
public function toXML(DOMElement $parent) {
assert('is_int($this->width)');
assert('is_int($this->height)');
assert('is_string($this->url)');
$doc = $parent->ownerDocument;
$e = $doc->createElementNS(SAML2_XML_mdui_UIInfo::NS, 'mdui:Logo');
$e->nodeValue = $this->url;
$e->setAttribute('width', (int)$this->width);
$e->setAttribute('height', (int)$this->height);
if (isset($this->lang)) {
$e->setAttribute('xml:lang', $this->lang);
}
$parent->appendChild($e);
return $e;
}
}
<?php
/**
* Class for handling the metadata extensions for login and discovery user interface
*
* @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-metadata-ui/v1.0/sstc-saml-metadata-ui-v1.0.pdf
* @package simpleSAMLphp
* @version $Id$
*/
class SAML2_XML_mdui_UIInfo {
/**
* The namespace used for the UIInfo extension.
*/
const NS = 'urn:oasis:names:tc:SAML:metadata:ui';
/**
* Array with child elements.
*
* The elements can be any of the other SAML2_XML_mdui_* elements.
*
* @var array
*/
public $children = array();
/**
* The DisplayName, as an array of language => translation.
*
* @var array
*/
public $DisplayName = array();
/**
* The Description, as an array of language => translation.
*
* @var array
*/
public $Description = array();
/**
* The InformationURL, as an array of language => url.
*
* @var array
*/
public $InformationURL = array();
/**
* The PrivacyStatementURL, as an array of language => url.
*
* @var array
*/
public $PrivacyStatementURL = array();
/**
* The Keywords, as an array of language => array of strings.
*
* @var array
*/
public $Keywords = array();
/**
* The Logo, as an array of associative arrays containing url, width, height, and optional lang.
*
* @var array
*/
public $Logo = array();
/**
* Create a UIInfo element.
*
* @param DOMElement|NULL $xml The XML element we should load.
*/
public function __construct(DOMElement $xml = NULL) {
if ($xml === NULL) {
return;
}
$this->DisplayName = SAML2_Utils::extractLocalizedStrings($xml, self::NS, 'DisplayName');
$this->Description = SAML2_Utils::extractLocalizedStrings($xml, self::NS, 'Description');
$this->InformationURL = SAML2_Utils::extractLocalizedStrings($xml, self::NS, 'InformationURL');
$this->PrivacyStatementURL = SAML2_Utils::extractLocalizedStrings($xml, self::NS, 'PrivacyStatementURL');
foreach (SAML2_Utils::xpQuery($xml, './*') as $node) {
if ($node->namespaceURI === self::NS) {
switch ($node->localName) {
case 'Keywords':
$this->Keywords[] = new SAML2_XML_mdui_Keywords($node);
break;
case 'Logo':
$this->Logo[] = new SAML2_XML_mdui_Logo($node);
break;
}
} else {
$this->children[] = new SAML2_XML_Chunk($node);
}
}
}
/**
* Convert this UIInfo to XML.
*
* @param DOMElement $parent The element we should append to.
*/
public function toXML(DOMElement $parent) {
assert('is_array($this->DisplayName)');
assert('is_array($this->InformationURL)');
assert('is_array($this->PrivacyStatementURL)');
assert('is_array($this->Keywords)');
assert('is_array($this->Logo)');
assert('is_array($this->children)');
if (!empty($this->DisplayName)
|| !empty($this->Description)
|| !empty($this->InformationURL)
|| !empty($this->PrivacyStatementURL)
|| !empty($this->Keywords)
|| !empty($this->Logo)
|| !empty($this->children)) {
$doc = $parent->ownerDocument;
$e = $doc->createElementNS(self::NS, 'mdui:UIInfo');
$parent->appendChild($e);
SAML2_Utils::addStrings($e, self::NS, 'mdui:DisplayName', TRUE, $this->DisplayName);
SAML2_Utils::addStrings($e, self::NS, 'mdui:Description', TRUE, $this->Description);
SAML2_Utils::addStrings($e, self::NS, 'mdui:InformationURL', TRUE, $this->InformationURL);
SAML2_Utils::addStrings($e, self::NS, 'mdui:PrivacyStatementURL', TRUE, $this->PrivacyStatementURL);
if (!empty($this->Keywords)) {
foreach ($this->Keywords as $child) {
$child->toXML($e);
}
}
if (!empty($this->Logo)) {
foreach ($this->Logo as $child) {
$child->toXML($e);
}
}
if (!empty($this->children)) {
foreach ($this->children as $child) {
$child->toXML($e);
}
}
}
return $e;
}
}
...@@ -118,6 +118,88 @@ class SimpleSAML_Metadata_SAMLBuilder { ...@@ -118,6 +118,88 @@ class SimpleSAML_Metadata_SAMLBuilder {
$e->Extensions[] = $s; $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;
}
} }
......
...@@ -97,6 +97,8 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -97,6 +97,8 @@ class SimpleSAML_Metadata_SAMLParser {
private $entityAttributes; private $entityAttributes;
private $attributes; private $attributes;
private $tags; private $tags;
private $uiInfo;
private $discoHints;
/** /**
...@@ -427,7 +429,14 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -427,7 +429,14 @@ class SimpleSAML_Metadata_SAMLParser {
if (!empty($this->entityAttributes)) { if (!empty($this->entityAttributes)) {
$metadata['EntityAttributes'] = $this->entityAttributes; $metadata['EntityAttributes'] = $this->entityAttributes;
} }
if (!empty($roleDescriptor['UIInfo'])) {
$metadata['UIInfo'] = $roleDescriptor['UIInfo'];
}
if (!empty($roleDescriptor['DiscoHints'])) {
$metadata['DiscoHints'] = $roleDescriptor['DiscoHints'];
}
} }
...@@ -739,6 +748,8 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -739,6 +748,8 @@ class SimpleSAML_Metadata_SAMLParser {
$ret['scope'] = $ext['scope']; $ret['scope'] = $ext['scope'];
$ret['tags'] = $ext['tags']; $ret['tags'] = $ext['tags'];
$ret['EntityAttributes'] = $ext['EntityAttributes']; $ret['EntityAttributes'] = $ext['EntityAttributes'];
$ret['UIInfo'] = $ext['UIInfo'];
$ret['DiscoHints'] = $ext['DiscoHints'];
return $ret; return $ret;
} }
...@@ -861,6 +872,8 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -861,6 +872,8 @@ class SimpleSAML_Metadata_SAMLParser {
'scope' => array(), 'scope' => array(),
'tags' => array(), 'tags' => array(),
'EntityAttributes' => array(), 'EntityAttributes' => array(),
'UIInfo' => array(),
'DiscoHints' => array(),
); );
foreach ($element->Extensions as $e) { foreach ($element->Extensions as $e) {
...@@ -869,7 +882,7 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -869,7 +882,7 @@ class SimpleSAML_Metadata_SAMLParser {
$ret['scope'][] = $e->scope; $ret['scope'][] = $e->scope;
continue; continue;
} }
// Entity Attributes are only allowed at entity level extensions // Entity Attributes are only allowed at entity level extensions
// and not at RoleDescriptor level // and not at RoleDescriptor level
if ($element instanceof SAML2_XML_md_EntityDescriptor) { if ($element instanceof SAML2_XML_md_EntityDescriptor) {
...@@ -901,7 +914,52 @@ class SimpleSAML_Metadata_SAMLParser { ...@@ -901,7 +914,52 @@ class SimpleSAML_Metadata_SAMLParser {
} }
} }
} }
// UIInfo elements are only allowed at RoleDescriptor level extensions
if ($element instanceof SAML2_XML_md_RoleDescriptor) {
if ($e instanceof SAML2_XML_mdui_UIInfo) {
$ret['UIInfo']['DisplayName'] = $e->DisplayName;
$ret['UIInfo']['Description'] = $e->Description;
$ret['UIInfo']['InformationURL'] = $e->InformationURL;
$ret['UIInfo']['PrivacyStatementURL'] = $e->PrivacyStatementURL;
foreach($e->Keywords as $uiItem) {
if (!($uiItem instanceof SAML2_XML_mdui_Keywords)
|| empty($uiItem->Keywords)
|| empty($uiItem->lang))
continue;
$ret['UIInfo']['Keywords'][$uiItem->lang] = $uiItem->Keywords;
}
foreach($e->Logo as $uiItem) {
if (!($uiItem instanceof SAML2_XML_mdui_Logo)
|| empty($uiItem->url)
|| empty($uiItem->height)
|| empty($uiItem->width))
continue;
$logo = array(
'url' => $uiItem->url,
'height' => $uiItem->height,
'width' => $uiItem->width,
);
if (!empty($uiItem->Lang)) {
$logo['lang'] = $uiItem->lang;
}
$ret['UIInfo']['Logo'][] = $logo;
}
}
}
// DiscoHints elements are only allowed at IDPSSODescriptor level extensions
if ($element instanceof SAML2_XML_md_IDPSSODescriptor) {
if ($e instanceof SAML2_XML_mdui_DiscoHints) {
$ret['DiscoHints']['IPHint'] = $e->IPHint;
$ret['DiscoHints']['DomainHint'] = $e->DomainHint;
$ret['DiscoHints']['GeolocationHint'] = $e->GeolocationHint;
}
}
if (!($e instanceof SAML2_XML_Chunk)) { if (!($e instanceof SAML2_XML_Chunk)) {
......
...@@ -105,6 +105,18 @@ try { ...@@ -105,6 +105,18 @@ try {
$metaArray['scope'] = $idpmeta->getArray('scope'); $metaArray['scope'] = $idpmeta->getArray('scope');
} }
if ($idpmeta->hasValue('EntityAttributes')) {
$metaArray['EntityAttributes'] = $idpmeta->getArray('EntityAttributes');
}
if ($idpmeta->hasValue('UIInfo')) {
$metaArray['UIInfo'] = $idpmeta->getArray('UIInfo');
}
if ($idpmeta->hasValue('DiscoHints')) {
$metaArray['DiscoHints'] = $idpmeta->getArray('DiscoHints');
}
$metaflat = '$metadata[' . var_export($idpentityid, TRUE) . '] = ' . var_export($metaArray, TRUE) . ';'; $metaflat = '$metadata[' . var_export($idpentityid, TRUE) . '] = ' . var_export($metaArray, TRUE) . ';';
$metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($idpentityid); $metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($idpentityid);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment