Skip to content
This repository was archived by the owner on Sep 19, 2022. It is now read-only.

Commit a2ca6ea

Browse files
author
Dominik Frantisek Bucik
committed
feat: 🎸 AuthProcFilter GenerateIdPAttributes
Filter for generating attributes based on authenticating IdP metadata.
1 parent 1f8bd75 commit a2ca6ea

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

config-templates/processFilterConfigurations-example.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,23 @@ Configuration options:
265265
'sp_name_qualifier' => 'https://login.cesnet.cz/proxy/',
266266
],
267267
```
268+
269+
## GenerateIdPAttributes
270+
271+
Gets metadata of the IdP specified by `idp_identifier_attribute` value and tries to set the specified keys from IdP metadata into attributes.
272+
273+
Configuration options:
274+
* `idp_identifier_attribute`: Attribute holding the identifier of the Authenticating IdP
275+
* `attribute_map`: Map of IdP metadata attributes, where keys are the colon separated keys that will be searched in IdP metadata and values are the destination attribute names.
276+
277+
```php
278+
20 => [
279+
'class' => 'perun:GenerateIdPAttributes',
280+
'idp_identifier_attribute' => 'sourceIdPEntityID',
281+
'attribute_map' => [
282+
'name:en' => 'sourceIdPName',
283+
'OrganizationName:en' => 'sourceIdPOrganizationName',
284+
'OrganizationURL:en' => 'sourceIdPOrganizationURL',
285+
],
286+
],
287+
```
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Module\perun\Auth\Process;
6+
7+
use SimpleSAML\Auth\ProcessingFilter;
8+
use SimpleSAML\Configuration;
9+
use SimpleSAML\Error\Exception;
10+
use SimpleSAML\Error\MetadataNotFound;
11+
use SimpleSAML\Logger;
12+
use SimpleSAML\Metadata\MetaDataStorageHandler;
13+
use SimpleSAML\Module\perun\PerunConstants;
14+
15+
/**
16+
* Generated attributes based on IdP metadata
17+
*/
18+
class GenerateIdPAttributes extends ProcessingFilter
19+
{
20+
public const STAGE = 'perun:GenerateIdPAttributes';
21+
public const DEBUG_PREFIX = self::STAGE . ' - ';
22+
23+
public const ATTRIBUTE_MAP = 'attribute_map';
24+
public const IDP_IDENTIFIER_ATTRIBUTE = 'idp_identifier_attribute';
25+
26+
public const SAML_SP_IDP = 'saml:sp:IdP';
27+
public const SAML20_IDP_REMOTE = 'saml20-idp-remote';
28+
29+
private $attributeMap;
30+
private $idpIdentifierAttribute;
31+
private $filterConfig;
32+
33+
public function __construct($config, $reserved)
34+
{
35+
parent::__construct($config, $reserved);
36+
$this->filterConfig = Configuration::loadFromArray($config);
37+
38+
$this->attributeMap = $this->filterConfig->getArray(self::ATTRIBUTE_MAP, []);
39+
if (empty($this->attributeMap)) {
40+
throw new Exception(
41+
self::DEBUG_PREFIX . 'Invalid configuration: no map of attributes for generation ' . 'has been configured. Use option \'' . self::ATTRIBUTE_MAP . '\' to configure the map of keys in ' . 'IDP metadata to names of attributes to generate.'
42+
);
43+
}
44+
45+
$this->idpIdentifierAttribute = $this->filterConfig->getString(self::IDP_IDENTIFIER_ATTRIBUTE, null);
46+
if (empty($this->idpIdentifierAttribute)) {
47+
Logger::debug(
48+
self::DEBUG_PREFIX . 'No name of attribute containing IDP identifier configured. '
49+
. 'Will use default entry in request object \'saml:sp:IdP\''
50+
);
51+
}
52+
}
53+
54+
public function process(&$request)
55+
{
56+
assert(is_array($request));
57+
58+
$sourceIdpMeta = null;
59+
$metadataHandler = MetaDataStorageHandler::getMetadataHandler();
60+
if (!empty($this->idpIdentifierAttribute)) {
61+
$idpIdentifier = $request[PerunConstants::ATTRIBUTES][$this->idpIdentifierAttribute][0] ?? null;
62+
if (!empty($idpIdentifier)) {
63+
try {
64+
$sourceIdpMeta = $metadataHandler->getMetaData($idpIdentifier, self::SAML20_IDP_REMOTE);
65+
} catch (MetadataNotFound $ex) {
66+
Logger::warning(self::DEBUG_PREFIX . 'Metadata for IDP \'' . $idpIdentifier . '\' not found.');
67+
}
68+
} else {
69+
Logger::debug(
70+
self::DEBUG_PREFIX . 'Could not extract IDP identifier from the attributes. Did you '
71+
. 'configure \'' . ExtractRequestAttribute::STAGE . '\' filter to be run before this one?'
72+
);
73+
}
74+
}
75+
76+
if (empty($sourceIdpMeta)) {
77+
Logger::debug(self::DEBUG_PREFIX . 'Trying to use key \'' . self::SAML_SP_IDP . '\' instead.');
78+
$idpIdentifier = $request[self::SAML_SP_IDP] ?? null;
79+
Logger::debug(self::DEBUG_PREFIX . 'Using \'' . $idpIdentifier . '\' as the IDP identifier');
80+
if (!empty($idpIdentifier)) {
81+
try {
82+
$sourceIdpMeta = $metadataHandler->getMetaData($idpIdentifier, self::SAML20_IDP_REMOTE);
83+
} catch (MetadataNotFound $exc) {
84+
// this should never happen
85+
Logger::warning(self::DEBUG_PREFIX . 'Metadata for IDP \'' . $idpIdentifier . '\' not found.');
86+
throw new Exception(self::DEBUG_PREFIX . 'Could not find metadata for the authenticating IdP');
87+
}
88+
} else {
89+
throw new Exception(self::DEBUG_PREFIX . 'Could not find identifier of the authenticating IDP');
90+
}
91+
}
92+
93+
foreach ($this->attributeMap as $sourceAttributeName => $destinationAttributeName) {
94+
$attributeNames = preg_split('/:/', $sourceAttributeName);
95+
$key = array_shift($attributeNames);
96+
97+
if (!isset($sourceIdpMeta[$key])) {
98+
continue;
99+
}
100+
$value = $sourceIdpMeta[$key];
101+
102+
foreach ($attributeNames as $attributeName) {
103+
if (!isset($value[$attributeName])) {
104+
continue;
105+
}
106+
$value = $value[$attributeName];
107+
}
108+
109+
if (!is_array($value)) {
110+
$value = [$value];
111+
}
112+
113+
if (!empty($value)) {
114+
$request[PerunConstants::ATTRIBUTES][$destinationAttributeName] = $value;
115+
Logger::debug(
116+
self::DEBUG_PREFIX . 'Added attribute from metadata with key \'' . $sourceAttributeName
117+
. '\' as attribute \'' . $destinationAttributeName . '\' with value \'' . implode(';', $value)
118+
);
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)