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

Commit 2a3d052

Browse files
committed
feat: updateUes - configurable identifiers
1 parent 9f7d31e commit 2a3d052

File tree

2 files changed

+199
-54
lines changed

2 files changed

+199
-54
lines changed

config-templates/module_perun.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,13 @@
278278
'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>',
279279
],
280280
],
281+
282+
'updateUes' => [
283+
'userIdentifiers' => [
284+
'eduPersonPrincipalName',
285+
'internalUserIdentifiers',
286+
'eduPersonUniqueId',
287+
// ...
288+
],
289+
],
281290
];

www/updateUes.php

Lines changed: 190 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,45 @@
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 CLASS_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+
1348
$adapter = Adapter::getInstance(Adapter::RPC);
1449
$token = file_get_contents('php://input');
1550

@@ -23,104 +58,205 @@
2358
$attrsToConversion = null;
2459
$perunUserId = null;
2560
$id = null;
26-
27-
const UES_ATTR_NMS = 'urn:perun:ues:attribute-def:def';
61+
$sourceIdpAttributeKey = null;
2862

2963
try {
3064
$challengeManager = new ChallengeManager();
3165
$claims = $challengeManager->decodeToken($token);
3266

33-
$attributesFromIdP = $claims['data']['attributes'];
34-
$attrMap = $claims['data']['attrMap'];
35-
$attrsToConversion = $claims['data']['attrsToConversion'];
36-
$perunUserId = $claims['data']['perunUserId'];
37-
$id = $claims['id'];
67+
$attributesFromIdP = $claims[DATA][ATTRIBUTES];
68+
$attrMap = $claims[DATA][ATTR_MAP];
69+
$attrsToConversion = $claims[DATA][ATTR_TO_CONVERSION];
70+
$perunUserId = $claims[DATA][PERUN_USER_ID];
71+
$id = $claims[ID];
3872
} catch (Exception $ex) {
39-
Logger::error('Perun.updateUes: An error occurred when the token was verifying.');
73+
Logger::error(CLASS_PREFIX . 'The token verification ended with an error.');
4074
http_response_code(400);
4175
exit;
4276
}
4377

4478
try {
45-
if (empty($attributesFromIdP['sourceIdPEntityID'][0])) {
46-
throw new Exception('perun/www/updateUes.php: Invalid attributes from Idp - sourceIdPEntityID is empty');
47-
}
79+
$config = Configuration::getConfig(CONFIG_FILE_NAME);
80+
$config = $config->getArray(CONFIG_SECTION, null);
81+
} catch (Exception $e) {
82+
$config = null;
83+
}
84+
85+
if (null === $config) {
86+
Logger::warning(CLASS_PREFIX . 'Configuration is missing. Using default values');
87+
}
88+
89+
$sourceIdpAttributeKey = empty($config[SOURCE_IDP_ATTRIBUTE_KEY]) ? SOURCE_IDP_ENTITY_ID : $config[SOURCE_IDP_ATTRIBUTE_KEY];
4890

49-
if (empty($attributesFromIdP['sourceIdPEppn'][0])) {
50-
throw new Exception('perun/www/updateUes.php: Invalid attributes from Idp - sourceIdPEppn is empty');
91+
if (null !== $config && !empty($config[USER_IDENTIFIERS] && is_array($config[USER_IDENTIFIERS]))) {
92+
$userIdentifiers = $config[USER_IDENTIFIERS];
93+
} else {
94+
$userIdentifiers = [EDU_PERSON_UNIQUE_ID, EDU_PERSON_PRINCIPAL_NAME, EDU_PERSON_TARGETED_ID, NAMEID, UID];
95+
}
96+
97+
try {
98+
if (empty($attributesFromIdP[$sourceIdpAttributeKey][0])) {
99+
throw new Exception(CLASS_PREFIX . 'Invalid attributes from Idp - \'' . $sourceIdpAttributeKey . '\' is empty');
51100
}
52101

53-
$userExtSource = $adapter->getUserExtSource(
54-
$attributesFromIdP['sourceIdPEntityID'][0],
55-
$attributesFromIdP['sourceIdPEppn'][0]
56-
);
102+
$extSourceName = $attributesFromIdP[$sourceIdpAttributeKey][0];
103+
Logger::debug(CLASS_PREFIX . 'Extracted extSourceName: \'' . $extSourceName . '\'');
104+
105+
$userExtSource = findUserExtSource($adapter, $extSourceName, $attributesFromIdP, $userIdentifiers);
57106
if (null === $userExtSource) {
58107
throw new Exception(
59-
'perun/www/updateUes.php: there is no UserExtSource with ExtSource ' . $attributesFromIdP['sourceIdPEntityID'][0] . ' and Login ' . $attributesFromIdP['sourceIdPEppn'][0]
108+
CLASS_PREFIX . 'There is no UserExtSource that could be used for user ' . $perunUserId . ' and ExtSource ' . $attributesFromIdP[$sourceIdpAttributeKey][0]
60109
);
61110
}
62111

63-
$attributesFromPerunRaw = $adapter->getUserExtSourceAttributes($userExtSource['id'], array_keys($attrMap));
112+
$attributesFromPerun = getAttributesFromPerun($adapter, $attrMap, $userExtSource);
113+
$attributesToUpdate = getAttributesToUpdate($attributesFromPerun, $attrMap, $attrsToConversion, $attributesFromIdP);
114+
115+
if (updateUserExtSource($adapter, $userExtSource, $attributesToUpdate)) {
116+
Logger::debug(CLASS_PREFIX . 'Updating UES for user with userId: ' . $perunUserId . ' was successful.');
117+
}
118+
} catch (\Exception $ex) {
119+
Logger::warning(
120+
CLASS_PREFIX . 'Updating UES for user with userId: ' . $perunUserId . ' was not successful: ' .
121+
$ex->getMessage()
122+
);
123+
}
124+
125+
function findUserExtSource($adapter, $extSourceName, $attributes, $userIdentifiers)
126+
{
127+
foreach ($attributes as $attrName => $attrValue) {
128+
if (!in_array($attrName, $userIdentifiers, true)) {
129+
Logger::debug(CLASS_PREFIX . 'Identifier \'' . $attrName . '\' not listed in userIdentifiers. Skipping');
130+
continue;
131+
}
132+
133+
if (is_array($attrValue)) {
134+
foreach ($attrValue as $extLogin) {
135+
$userExtSource = getUserExtSource($adapter, $extSourceName, $extLogin);
136+
137+
if (null !== $userExtSource) {
138+
return $userExtSource;
139+
}
140+
}
141+
} elseif (is_string($attrValue)) {
142+
$userExtSource = getUserExtSource($adapter, $attrValue, $extLogin);
143+
144+
if (null !== $userExtSource) {
145+
return $userExtSource;
146+
}
147+
}
148+
}
149+
150+
return null;
151+
}
152+
153+
function getUserExtSource($adapter, $extSourceName, $extLogin)
154+
{
155+
try {
156+
return $adapter->getUserExtSource($extSourceName, $extLogin);
157+
} catch (SimpleSAML\Module\perun\Exception $ex) {
158+
Logger::debug(CLASS_PREFIX . 'Caught exception when fetching user ext source, probably does not exist.');
159+
Logger::debug(CLASS_PREFIX . $ex->getMessage());
160+
161+
return null;
162+
}
163+
}
164+
165+
function getAttributesFromPerun($adapter, $attrMap, $userExtSource): array
166+
{
167+
$attributesFromPerunRaw = $adapter->getUserExtSourceAttributes($userExtSource[ID], array_keys($attrMap));
64168
$attributesFromPerun = [];
65-
foreach ($attributesFromPerunRaw as $attributeFromPerunRaw) {
66-
$attributesFromPerun[$attributeFromPerunRaw['name']] = $attributeFromPerunRaw;
169+
170+
foreach ($attributesFromPerunRaw as $rawAttribute) {
171+
if (!empty($rawAttribute[NAME])) {
172+
$attributesFromPerun[$rawAttribute[NAME]] = $rawAttribute;
173+
}
67174
}
68175

69176
if (null === $attributesFromPerun) {
70-
throw new Exception('perun/www/updateUes.php: getting attributes was not successful.');
177+
throw new Exception(CLASS_PREFIX . 'Getting attributes was not successful.');
71178
}
72179

180+
return $attributesFromPerun;
181+
}
182+
183+
function getAttributesToUpdate($attributesFromPerun, $attrMap, $attrsToConversion, $attributesFromIdP): array
184+
{
73185
$attributesToUpdate = [];
74186

75187
foreach ($attributesFromPerun as $attribute) {
76-
$attrName = $attribute['name'];
188+
$attrName = $attribute[NAME];
77189

78-
if (isset($attrMap[$attrName], $attributesFromIdP[$attrMap[$attrName]])) {
79-
$attr = $attributesFromIdP[$attrMap[$attrName]];
190+
$mappedAttributeName = !empty($attrMap[$attrName]) ? $attrMap[$attrName] : null;
191+
$idpAttribute = !empty($attributesFromIdP[$attrMap[$attrName]]) ?
192+
$attributesFromIdP[$attrMap[$attrName]] : null;
80193

194+
if (null !== $mappedAttributeName && null !== $idpAttribute) {
81195
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;
196+
$idpAttribute = serializeAsString($idpAttribute);
90197
}
91198

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;
199+
if (isSimpleType($attribute[TYPE])) {
200+
$valueFromIdP = $idpAttribute[0];
201+
} elseif (isComplexType($attribute[TYPE])) {
202+
$valueFromIdP = $idpAttribute;
98203
} else {
99-
throw new Exception('perun/www/updateUes.php: unsupported type of attribute.');
204+
throw new Exception(CLASS_PREFIX . 'Unsupported type of attribute.');
100205
}
101-
if ($valueFromIdP !== $attribute['value']) {
102-
$attribute['value'] = $valueFromIdP;
103-
$attribute['namespace'] = UES_ATTR_NMS;
104-
array_push($attributesToUpdate, $attribute);
206+
207+
if ($valueFromIdP !== $attribute[VALUE]) {
208+
$attribute[VALUE] = $valueFromIdP;
209+
$attribute[NAMESPACE_KEY] = UES_ATTR_NMS;
210+
$attributesToUpdate[] = $attribute;
105211
}
106212
}
107213
}
108214

215+
return $attributesToUpdate;
216+
}
217+
218+
function updateUserExtSource($adapter, $userExtSource, $attributesToUpdate): bool
219+
{
109220
$attributesToUpdateFinal = [];
221+
110222
if (!empty($attributesToUpdate)) {
111223
foreach ($attributesToUpdate as $attribute) {
112-
$attribute['name'] = UES_ATTR_NMS . ':' . $attribute['friendlyName'];
113-
array_push($attributesToUpdateFinal, $attribute);
224+
$attribute[NAME] = UES_ATTR_NMS . ':' . $attribute[FRIENDLY_NAME];
225+
$attributesToUpdateFinal[] = $attribute;
114226
}
115-
$adapter->setUserExtSourceAttributes($userExtSource['id'], $attributesToUpdateFinal);
227+
228+
$adapter->setUserExtSourceAttributes($userExtSource[ID], $attributesToUpdateFinal);
116229
}
117230

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

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-
);
233+
return true;
234+
}
235+
236+
function isSimpleType($attributeType): bool
237+
{
238+
return strpos($attributeType, STRING_TYPE)
239+
|| strpos($attributeType, INTEGER_TYPE)
240+
|| strpos($attributeType, BOOLEAN_TYPE);
241+
}
242+
243+
function isComplexType($attributeType): bool
244+
{
245+
return strpos($attributeType, ARRAY_TYPE) ||
246+
strpos($attributeType, MAP_TYPE);
247+
}
248+
249+
function serializeAsString($idpAttribute)
250+
{
251+
$arrayAsString = [''];
252+
253+
foreach ($idpAttribute as $value) {
254+
$arrayAsString[0] .= $value . ';';
255+
}
256+
257+
if (!empty($arrayAsString[0])) {
258+
$arrayAsString[0] = substr($arrayAsString[0], 0, -1);
259+
}
260+
261+
return $arrayAsString;
126262
}

0 commit comments

Comments
 (0)