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

Commit 8221707

Browse files
author
Dominik František Bučík
authored
Merge pull request #245 from BaranekD/ues_fix
feat: updateUes - configurable identifiers
2 parents a5d7090 + ce7b09f commit 8221707

File tree

2 files changed

+229
-59
lines changed

2 files changed

+229
-59
lines changed

config-templates/module_perun.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,13 @@
280280
'cs' => '<p>Služba je v testovacím režimu.<br/>Služba v testovacím režimu nemusí správně fungovat.</p><p>Pokračujte zmáčknutím tlačítka.</p>',
281281
],
282282
],
283+
284+
'updateUes' => [
285+
'userIdentifiers' => [
286+
'eduPersonPrincipalName',
287+
'internalUserIdentifiers',
288+
'eduPersonUniqueId',
289+
// ...
290+
],
291+
],
283292
];

www/updateUes.php

Lines changed: 220 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,72 @@
66
* Script for updating UES in separate thread.
77
*/
88

9+
use SimpleSAML\Configuration;
910
use SimpleSAML\Logger;
1011
use SimpleSAML\Module\perun\Adapter;
1112
use SimpleSAML\Module\perun\ChallengeManager;
1213

14+
const DEBUG_PREFIX = 'perun/www/updateUes.php: ';
15+
const CONFIG_FILE_NAME = 'module_perun.php';
16+
const CONFIG_SECTION = 'updateUes';
17+
const SOURCE_IDP_ATTRIBUTE_KEY = 'sourceIdPAttributeKey';
18+
19+
const USER_IDENTIFIERS = 'userIdentifiers';
20+
const SOURCE_IDP_ENTITY_ID = 'sourceIdPEntityID';
21+
22+
const NAME = 'name';
23+
const FRIENDLY_NAME = 'friendlyName';
24+
const ID = 'id';
25+
const VALUE = 'value';
26+
const NAMESPACE_KEY = 'namespace';
27+
const UES_ATTR_NMS = 'urn:perun:ues:attribute-def:def';
28+
29+
const TYPE = 'type';
30+
const STRING_TYPE = 'String';
31+
const INTEGER_TYPE = 'Integer';
32+
const BOOLEAN_TYPE = 'Boolean';
33+
const ARRAY_TYPE = 'Array';
34+
const MAP_TYPE = 'Map';
35+
36+
const DATA = 'data';
37+
const ATTRIBUTES = 'attributes';
38+
const ATTR_MAP = 'attrMap';
39+
const ATTR_TO_CONVERSION = 'attrsToConversion';
40+
const PERUN_USER_ID = 'perunUserId';
41+
42+
const EDU_PERSON_UNIQUE_ID = 'eduPersonUniqueId';
43+
const EDU_PERSON_PRINCIPAL_NAME = 'eduPersonPrincipalName';
44+
const EDU_PERSON_TARGETED_ID = 'eduPersonTargetedID';
45+
const NAMEID = 'nameid';
46+
const UID = 'uid';
47+
48+
function getDefaultConfig(): array
49+
{
50+
return [
51+
SOURCE_IDP_ATTRIBUTE_KEY => SOURCE_IDP_ENTITY_ID,
52+
USER_IDENTIFIERS => [EDU_PERSON_UNIQUE_ID, EDU_PERSON_PRINCIPAL_NAME, EDU_PERSON_TARGETED_ID, NAMEID, UID],
53+
];
54+
}
55+
56+
function getConfiguration()
57+
{
58+
$config = getDefaultConfig();
59+
try {
60+
$configuration = Configuration::getConfig(CONFIG_FILE_NAME);
61+
$localConfig = $configuration->getArray(CONFIG_SECTION, null);
62+
if (!empty($localConfig)) {
63+
$config = $localConfig;
64+
} else {
65+
Logger::warning(DEBUG_PREFIX . 'Configuration is missing. Using default values');
66+
}
67+
} catch (Exception $e) {
68+
Logger::warning(DEBUG_PREFIX . 'Configuration is invalid. Using default values');
69+
//OK, we will use the default config
70+
}
71+
72+
return $config;
73+
}
74+
1375
$adapter = Adapter::getInstance(Adapter::RPC);
1476
$token = file_get_contents('php://input');
1577

@@ -20,107 +82,206 @@
2082

2183
$attributesFromIdP = null;
2284
$attrMap = null;
23-
$attrsToConversion = null;
85+
$serializedAttributes = null;
2486
$perunUserId = null;
2587
$id = null;
26-
27-
const UES_ATTR_NMS = 'urn:perun:ues:attribute-def:def';
88+
$sourceIdpAttribute = null;
2889

2990
try {
3091
$challengeManager = new ChallengeManager();
3192
$claims = $challengeManager->decodeToken($token);
3293

33-
$attributesFromIdP = $claims['data']['attributes'];
34-
$attrMap = $claims['data']['attrMap'];
35-
$attrsToConversion = $claims['data']['attrsToConversion'];
36-
$perunUserId = $claims['data']['perunUserId'];
37-
$id = $claims['id'];
94+
$attributesFromIdP = $claims[DATA][ATTRIBUTES];
95+
$attrMap = $claims[DATA][ATTR_MAP];
96+
$serializedAttributes = $claims[DATA][ATTR_TO_CONVERSION];
97+
$perunUserId = $claims[DATA][PERUN_USER_ID];
98+
$id = $claims[ID];
3899
} catch (Exception $ex) {
39-
Logger::error('Perun.updateUes: An error occurred when the token was verifying.');
100+
Logger::error(DEBUG_PREFIX . 'The token verification ended with an error.');
40101
http_response_code(400);
41102
exit;
42103
}
43104

105+
$config = getConfiguration();
106+
107+
$sourceIdpAttribute = $config[SOURCE_IDP_ATTRIBUTE_KEY];
108+
$identifierAttributes = $config[USER_IDENTIFIERS];
109+
44110
try {
45-
if (empty($attributesFromIdP['sourceIdPEntityID'][0])) {
46-
throw new Exception('perun/www/updateUes.php: Invalid attributes from Idp - sourceIdPEntityID is empty');
111+
if (empty($attributesFromIdP[$sourceIdpAttribute][0])) {
112+
throw new Exception(
113+
DEBUG_PREFIX . 'Invalid attributes from IdP - Attribute \'' . $sourceIdpAttribute . '\' is empty'
114+
);
47115
}
48116

49-
if (empty($attributesFromIdP['sourceIdPEppn'][0])) {
50-
throw new Exception('perun/www/updateUes.php: Invalid attributes from Idp - sourceIdPEppn is empty');
51-
}
117+
$extSourceName = $attributesFromIdP[$sourceIdpAttribute][0];
118+
Logger::debug(DEBUG_PREFIX . 'Extracted extSourceName: \'' . $extSourceName . '\'');
52119

53-
$userExtSource = $adapter->getUserExtSource(
54-
$attributesFromIdP['sourceIdPEntityID'][0],
55-
$attributesFromIdP['sourceIdPEppn'][0]
56-
);
120+
$userExtSource = findUserExtSource($adapter, $extSourceName, $attributesFromIdP, $identifierAttributes);
57121
if (null === $userExtSource) {
58122
throw new Exception(
59-
'perun/www/updateUes.php: there is no UserExtSource with ExtSource ' . $attributesFromIdP['sourceIdPEntityID'][0] . ' and Login ' . $attributesFromIdP['sourceIdPEppn'][0]
123+
DEBUG_PREFIX . 'There is no UserExtSource that could be used for user ' . $perunUserId . ' and IdP ' . $extSourceName
60124
);
61125
}
62126

63-
$attributesFromPerunRaw = $adapter->getUserExtSourceAttributes($userExtSource['id'], array_keys($attrMap));
127+
$attributesFromPerun = getAttributesFromPerun($adapter, $attrMap, $userExtSource);
128+
$attributesToUpdate = getAttributesToUpdate(
129+
$attributesFromPerun,
130+
$attrMap,
131+
$serializedAttributes,
132+
$attributesFromIdP
133+
);
134+
135+
if (updateUserExtSource($adapter, $userExtSource, $attributesToUpdate)) {
136+
Logger::debug(DEBUG_PREFIX . 'Updating UES for user with userId: ' . $perunUserId . ' was successful.');
137+
}
138+
} catch (\Exception $ex) {
139+
Logger::warning(
140+
DEBUG_PREFIX . 'Updating UES for user with userId: ' . $perunUserId . ' was not successful: ' .
141+
$ex->getMessage()
142+
);
143+
}
144+
145+
function findUserExtSource($adapter, $extSourceName, $attributesFromIdp, $identifierAttributes)
146+
{
147+
foreach ($attributesFromIdp as $attrName => $attrValue) {
148+
if (!in_array($attrName, $identifierAttributes, true)) {
149+
Logger::debug(DEBUG_PREFIX . 'Identifier \'' . $attrName . '\' not listed in userIdentifiers. Skipping');
150+
continue;
151+
}
152+
153+
if (!is_array($attrValue)) {
154+
$attrValue = [$attrValue];
155+
}
156+
157+
foreach ($attrValue as $extLogin) {
158+
$userExtSource = getUserExtSource($adapter, $extSourceName, $extLogin);
159+
160+
if (null !== $userExtSource) {
161+
Logger::debug(
162+
DEBUG_PREFIX . 'Found user ext source for combination extSourceName \''
163+
. $extSourceName . '\' and extLogin \'' . $extLogin . '\''
164+
);
165+
166+
return $userExtSource;
167+
}
168+
}
169+
}
170+
171+
return null;
172+
}
173+
174+
function getUserExtSource($adapter, $extSourceName, $extLogin)
175+
{
176+
try {
177+
return $adapter->getUserExtSource($extSourceName, $extLogin);
178+
} catch (SimpleSAML\Module\perun\Exception $ex) {
179+
Logger::debug(DEBUG_PREFIX . 'Caught exception when fetching user ext source, probably does not exist.');
180+
Logger::debug(DEBUG_PREFIX . $ex->getMessage());
181+
182+
return null;
183+
}
184+
}
185+
186+
function getAttributesFromPerun($adapter, $attrMap, $userExtSource): array
187+
{
64188
$attributesFromPerun = [];
65-
foreach ($attributesFromPerunRaw as $attributeFromPerunRaw) {
66-
$attributesFromPerun[$attributeFromPerunRaw['name']] = $attributeFromPerunRaw;
189+
$attributesFromPerunRaw = $adapter->getUserExtSourceAttributes($userExtSource[ID], array_keys($attrMap));
190+
if (empty($attributesFromPerunRaw)) {
191+
throw new Exception(DEBUG_PREFIX . 'Getting attributes for UES was not successful.');
67192
}
68193

69-
if (null === $attributesFromPerun) {
70-
throw new Exception('perun/www/updateUes.php: getting attributes was not successful.');
194+
foreach ($attributesFromPerunRaw as $rawAttribute) {
195+
if (!empty($rawAttribute[NAME])) {
196+
$attributesFromPerun[$rawAttribute[NAME]] = $rawAttribute;
197+
}
198+
}
199+
200+
if (empty($attributesFromPerun)) {
201+
throw new Exception(DEBUG_PREFIX . 'Getting attributes for UES was not successful.');
71202
}
72203

204+
return $attributesFromPerun;
205+
}
206+
207+
function getAttributesToUpdate($attributesFromPerun, $attrMap, $serializedAttributes, $attributesFromIdP): array
208+
{
73209
$attributesToUpdate = [];
74210

75211
foreach ($attributesFromPerun as $attribute) {
76-
$attrName = $attribute['name'];
77-
78-
if (isset($attrMap[$attrName], $attributesFromIdP[$attrMap[$attrName]])) {
79-
$attr = $attributesFromIdP[$attrMap[$attrName]];
80-
81-
if (in_array($attrName, $attrsToConversion, true)) {
82-
$arrayAsString = [''];
83-
foreach ($attr as $value) {
84-
$arrayAsString[0] .= $value . ';';
85-
}
86-
if (!empty($arrayAsString[0])) {
87-
$arrayAsString[0] = substr($arrayAsString[0], 0, -1);
88-
}
89-
$attr = $arrayAsString;
212+
$attrName = $attribute[NAME];
213+
214+
$mappedAttributeName = !empty($attrMap[$attrName]) ? $attrMap[$attrName] : null;
215+
$idpAttribute = !empty($attributesFromIdP[$attrMap[$attrName]]) ?
216+
$attributesFromIdP[$attrMap[$attrName]] : null;
217+
218+
if (null !== $mappedAttributeName && null !== $idpAttribute) {
219+
if (in_array($attrName, $serializedAttributes, true)) {
220+
$idpAttribute = serializeAsString($idpAttribute);
90221
}
91222

92-
if (strpos($attribute['type'], 'String') ||
93-
strpos($attribute['type'], 'Integer') ||
94-
strpos($attribute['type'], 'Boolean')) {
95-
$valueFromIdP = $attr[0];
96-
} elseif (strpos($attribute['type'], 'Array') || strpos($attribute['type'], 'Map')) {
97-
$valueFromIdP = $attr;
223+
if (isSimpleType($attribute[TYPE])) {
224+
$valueFromIdP = $idpAttribute[0];
225+
} elseif (isComplexType($attribute[TYPE])) {
226+
$valueFromIdP = $idpAttribute;
98227
} else {
99-
throw new Exception('perun/www/updateUes.php: unsupported type of attribute.');
228+
Logger::debug(DEBUG_PREFIX . 'Unsupported type of attribute.');
229+
continue;
100230
}
101-
if ($valueFromIdP !== $attribute['value']) {
102-
$attribute['value'] = $valueFromIdP;
103-
$attribute['namespace'] = UES_ATTR_NMS;
104-
array_push($attributesToUpdate, $attribute);
231+
232+
if ($valueFromIdP !== $attribute[VALUE]) {
233+
$attribute[VALUE] = $valueFromIdP;
234+
$attribute[NAMESPACE_KEY] = UES_ATTR_NMS;
235+
$attributesToUpdate[] = $attribute;
105236
}
106237
}
107238
}
108239

240+
return $attributesToUpdate;
241+
}
242+
243+
function updateUserExtSource($adapter, $userExtSource, $attributesToUpdate): bool
244+
{
109245
$attributesToUpdateFinal = [];
246+
110247
if (!empty($attributesToUpdate)) {
111248
foreach ($attributesToUpdate as $attribute) {
112-
$attribute['name'] = UES_ATTR_NMS . ':' . $attribute['friendlyName'];
113-
array_push($attributesToUpdateFinal, $attribute);
249+
$attribute[NAME] = UES_ATTR_NMS . ':' . $attribute[FRIENDLY_NAME];
250+
$attributesToUpdateFinal[] = $attribute;
114251
}
115-
$adapter->setUserExtSourceAttributes($userExtSource['id'], $attributesToUpdateFinal);
252+
253+
$adapter->setUserExtSourceAttributes($userExtSource[ID], $attributesToUpdateFinal);
116254
}
117255

118-
$adapter->updateUserExtSourceLastAccess($userExtSource['id']);
256+
$adapter->updateUserExtSourceLastAccess($userExtSource[ID]);
119257

120-
Logger::debug('perun/www/updateUes.php: Updating UES for user with userId: ' . $perunUserId . ' was successful.');
121-
} catch (\Exception $ex) {
122-
Logger::warning(
123-
'perun/www/updateUes.php: Updating UES for user with userId: ' . $perunUserId . ' was not successful: ' .
124-
$ex->getMessage()
125-
);
258+
return true;
259+
}
260+
261+
function isSimpleType($attributeType): bool
262+
{
263+
return strpos($attributeType, STRING_TYPE)
264+
|| strpos($attributeType, INTEGER_TYPE)
265+
|| strpos($attributeType, BOOLEAN_TYPE);
266+
}
267+
268+
function isComplexType($attributeType): bool
269+
{
270+
return strpos($attributeType, ARRAY_TYPE) ||
271+
strpos($attributeType, MAP_TYPE);
272+
}
273+
274+
function serializeAsString($idpAttribute): array
275+
{
276+
$arrayAsString = [''];
277+
278+
foreach ($idpAttribute as $value) {
279+
$arrayAsString[0] .= $value . ';';
280+
}
281+
282+
if (!empty($arrayAsString[0])) {
283+
$arrayAsString[0] = substr($arrayAsString[0], 0, -1);
284+
}
285+
286+
return $arrayAsString;
126287
}

0 commit comments

Comments
 (0)