|
6 | 6 | * Script for updating UES in separate thread. |
7 | 7 | */ |
8 | 8 |
|
| 9 | +use SimpleSAML\Configuration; |
9 | 10 | use SimpleSAML\Logger; |
10 | 11 | use SimpleSAML\Module\perun\Adapter; |
11 | 12 | use SimpleSAML\Module\perun\ChallengeManager; |
12 | 13 |
|
| 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 | + |
13 | 75 | $adapter = Adapter::getInstance(Adapter::RPC); |
14 | 76 | $token = file_get_contents('php://input'); |
15 | 77 |
|
|
20 | 82 |
|
21 | 83 | $attributesFromIdP = null; |
22 | 84 | $attrMap = null; |
23 | | -$attrsToConversion = null; |
| 85 | +$serializedAttributes = null; |
24 | 86 | $perunUserId = null; |
25 | 87 | $id = null; |
26 | | - |
27 | | -const UES_ATTR_NMS = 'urn:perun:ues:attribute-def:def'; |
| 88 | +$sourceIdpAttribute = null; |
28 | 89 |
|
29 | 90 | try { |
30 | 91 | $challengeManager = new ChallengeManager(); |
31 | 92 | $claims = $challengeManager->decodeToken($token); |
32 | 93 |
|
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]; |
38 | 99 | } 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.'); |
40 | 101 | http_response_code(400); |
41 | 102 | exit; |
42 | 103 | } |
43 | 104 |
|
| 105 | +$config = getConfiguration(); |
| 106 | + |
| 107 | +$sourceIdpAttribute = $config[SOURCE_IDP_ATTRIBUTE_KEY]; |
| 108 | +$identifierAttributes = $config[USER_IDENTIFIERS]; |
| 109 | + |
44 | 110 | 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 | + ); |
47 | 115 | } |
48 | 116 |
|
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 . '\''); |
52 | 119 |
|
53 | | - $userExtSource = $adapter->getUserExtSource( |
54 | | - $attributesFromIdP['sourceIdPEntityID'][0], |
55 | | - $attributesFromIdP['sourceIdPEppn'][0] |
56 | | - ); |
| 120 | + $userExtSource = findUserExtSource($adapter, $extSourceName, $attributesFromIdP, $identifierAttributes); |
57 | 121 | if (null === $userExtSource) { |
58 | 122 | 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 |
60 | 124 | ); |
61 | 125 | } |
62 | 126 |
|
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 | +{ |
64 | 188 | $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.'); |
67 | 192 | } |
68 | 193 |
|
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.'); |
71 | 202 | } |
72 | 203 |
|
| 204 | + return $attributesFromPerun; |
| 205 | +} |
| 206 | + |
| 207 | +function getAttributesToUpdate($attributesFromPerun, $attrMap, $serializedAttributes, $attributesFromIdP): array |
| 208 | +{ |
73 | 209 | $attributesToUpdate = []; |
74 | 210 |
|
75 | 211 | 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); |
90 | 221 | } |
91 | 222 |
|
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; |
98 | 227 | } else { |
99 | | - throw new Exception('perun/www/updateUes.php: unsupported type of attribute.'); |
| 228 | + Logger::debug(DEBUG_PREFIX . 'Unsupported type of attribute.'); |
| 229 | + continue; |
100 | 230 | } |
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; |
105 | 236 | } |
106 | 237 | } |
107 | 238 | } |
108 | 239 |
|
| 240 | + return $attributesToUpdate; |
| 241 | +} |
| 242 | + |
| 243 | +function updateUserExtSource($adapter, $userExtSource, $attributesToUpdate): bool |
| 244 | +{ |
109 | 245 | $attributesToUpdateFinal = []; |
| 246 | + |
110 | 247 | if (!empty($attributesToUpdate)) { |
111 | 248 | 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; |
114 | 251 | } |
115 | | - $adapter->setUserExtSourceAttributes($userExtSource['id'], $attributesToUpdateFinal); |
| 252 | + |
| 253 | + $adapter->setUserExtSourceAttributes($userExtSource[ID], $attributesToUpdateFinal); |
116 | 254 | } |
117 | 255 |
|
118 | | - $adapter->updateUserExtSourceLastAccess($userExtSource['id']); |
| 256 | + $adapter->updateUserExtSourceLastAccess($userExtSource[ID]); |
119 | 257 |
|
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; |
126 | 287 | } |
0 commit comments