diff --git a/docs/simplesamlphp-authproc.txt b/docs/simplesamlphp-authproc.txt index d8122894b40979ebbe9ff568dad4657a493849f5..d6b556ef94915068c6875f1966d59f74e09d0659 100644 --- a/docs/simplesamlphp-authproc.txt +++ b/docs/simplesamlphp-authproc.txt @@ -143,6 +143,7 @@ The following filters are included in the simpleSAMLphp distribution: - [`core:WarnShortSSOInterval`](./core:authproc_warnshortssointerval): Give a warning if the user logs into the same SP twice within a few seconds. - [`preprodwarning:Warning`](./preprodwarning:warning): Warn the user about accessing a test IdP. - [`saml:AttributeNameID`](./saml:nameid): Generate custom NameID with the value of an attribute. +- [`saml:NameIDAttribute`](./saml:nameidattribute): Create an attribute based on the NameID we receive from the IdP. - [`saml:PersistentNameID`](./saml:nameid): Generate persistent NameID from an attribute. - [`saml:TransientNameID`](./saml:nameid): Generate transient NameID. diff --git a/modules/saml/docs/nameidattribute.txt b/modules/saml/docs/nameidattribute.txt new file mode 100644 index 0000000000000000000000000000000000000000..5bc440985cb8fa02d4d12f8ceba790217308fe40 --- /dev/null +++ b/modules/saml/docs/nameidattribute.txt @@ -0,0 +1,67 @@ +`saml:NameIDAttribute` +====================== + +Filter that extracts the NameID we received in the authentication response and adds it as an attribute. + +Parameters +---------- + +`attribute` +: The name of the attribute we should create. + The default is `nameid`. + +`format` +: The format string for the attribute. + The default is `%I!%S!%V`. + +: The format string accepts the following replacements: + + * `%I`: The IdP that issued the NameID. + This will be the `NameQualifier` element of the NameID if it is present, or the entity ID of the IdP we received the response from if not. + * `%S`: The SP the NameID was issued to. + This will be the `SPNameQualifier` element of the NameID if it is present, or the entity ID of this SP otherwise. + * `%V`: The value of the NameID. + * `%F`: The format of the NameID. + * `%%`: Will be replaced with a single `%`. + +Examples +-------- + +Minimal configuration: + + 'default-sp' => array( + 'saml:SP', + 'authproc' => array( + 20 => 'saml:NameIDAttribute', + ), + ), + +Custom attribute name: + + 'default-sp' => array( + 'saml:SP', + 'authproc' => array( + 20 => array( + 'class' => 'saml:NameIDAttribute', + 'attribute' => 'someattributename', + ), + ), + ), + +Only extract the value of the NameID. + + 'default-sp' => array( + 'saml:SP', + 'authproc' => array( + 20 => array( + 'class' => 'saml:NameIDAttribute', + 'format' => '%V', + ), + ), + ), + +See also +-------- + + * [The description of the `saml:SP` authentication source.](./saml:sp) + * [How to generate various NameIDs on the IdP.](./saml:nameid) diff --git a/modules/saml/lib/Auth/Process/NameIDAttribute.php b/modules/saml/lib/Auth/Process/NameIDAttribute.php new file mode 100644 index 0000000000000000000000000000000000000000..26640e257d557bed0cb3859ad3bd14227b2ec06c --- /dev/null +++ b/modules/saml/lib/Auth/Process/NameIDAttribute.php @@ -0,0 +1,138 @@ +<?php + +/** + * Authproc filter to create an attribute from a NameID. + * + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_saml_Auth_Process_NameIDAttribute extends SimpleSAML_Auth_ProcessingFilter { + + /** + * The attribute we should save the NameID in. + * + * @var string + */ + private $attribute; + + + /** + * The format of the NameID in the attribute. + * + * @var array + */ + private $format; + + + /** + * Initialize this filter, parse configuration. + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct($config, $reserved) { + parent::__construct($config, $reserved); + assert('is_array($config)'); + + if (isset($config['attribute'])) { + $this->attribute = (string)$config['attribute']; + } else { + $this->attribute = 'nameid'; + } + + if (isset($config['format'])) { + $format = (string)$config['format']; + } else { + $format = '%I!%S!%V'; + } + + $this->format = self::parseFormat($format); + } + + + /** + * Parse a NameID format string into an array. + * + * @param string $format The format string. + * @return array The format string broken into its individual components. + */ + private static function parseFormat($format) { + assert('is_string($format)'); + + $ret = array(); + $pos = 0; + while ( ($next = strpos($format, '%', $pos)) !== FALSE) { + $ret[] = substr($format, $pos, $next - $pos); + + $replacement = $format[$next + 1]; + switch ($replacement) { + case 'F': + $ret[] = 'Format'; + break; + case 'I': + $ret[] = 'NameQualifier'; + break; + case 'S': + $ret[] = 'SPNameQualifier'; + break; + case 'V': + $ret[] = 'Value'; + break; + case '%': + $ret[] = '%'; + break; + default: + throw new SimpleSAML_Error_Exception('NameIDAttribute: Invalid replacement: "%' . $replacement . '"'); + } + + $pos = $next + 2; + } + $ret[] = substr($format, $pos); + + return $ret; + } + + + /** + * Convert NameID to attribute. + * + * @param array &$state The request state. + */ + public function process(&$state) { + assert('is_array($state)'); + assert('isset($state["Source"]["entityid"])'); + assert('isset($state["Destination"]["entityid"])'); + + if (!isset($state['saml:sp:State']['LogoutState']['saml:logout:NameID'])) { + return; + } + + $rep = $state['saml:sp:State']['LogoutState']['saml:logout:NameID']; + assert('isset($rep["Value"])'); + + $rep['%'] = '%'; + if (!isset($rep['Format'])) { + $rep['Format'] = SAML2_Const::NAMEID_UNSPECIFIED; + } + if (!isset($rep['NameQualifier'])) { + $rep['NameQualifier'] = $state['Source']['entityid']; + } + if (!isset($rep['SPNameQualifier'])) { + $rep['SPNameQualifier'] = $state['Destination']['entityid']; + } + + $value = ''; + $isString = TRUE; + foreach ($this->format as $element) { + if ($isString) { + $value .= $element; + } else { + $value .= $rep[$element]; + } + $isString = !$isString; + } + + $state['Attributes'][$this->attribute] = array($value); + } + +}