|
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 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 | + |
13 | 48 | $adapter = Adapter::getInstance(Adapter::RPC); |
14 | 49 | $token = file_get_contents('php://input'); |
15 | 50 |
|
|
23 | 58 | $attrsToConversion = null; |
24 | 59 | $perunUserId = null; |
25 | 60 | $id = null; |
26 | | - |
27 | | -const UES_ATTR_NMS = 'urn:perun:ues:attribute-def:def'; |
| 61 | +$sourceIdpAttributeKey = null; |
28 | 62 |
|
29 | 63 | try { |
30 | 64 | $challengeManager = new ChallengeManager(); |
31 | 65 | $claims = $challengeManager->decodeToken($token); |
32 | 66 |
|
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]; |
38 | 72 | } 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.'); |
40 | 74 | http_response_code(400); |
41 | 75 | exit; |
42 | 76 | } |
43 | 77 |
|
44 | 78 | 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]; |
48 | 90 |
|
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'); |
51 | 100 | } |
52 | 101 |
|
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); |
57 | 106 | if (null === $userExtSource) { |
58 | 107 | 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] |
60 | 109 | ); |
61 | 110 | } |
62 | 111 |
|
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)); |
64 | 168 | $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 | + } |
67 | 174 | } |
68 | 175 |
|
69 | 176 | 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.'); |
71 | 178 | } |
72 | 179 |
|
| 180 | + return $attributesFromPerun; |
| 181 | +} |
| 182 | + |
| 183 | +function getAttributesToUpdate($attributesFromPerun, $attrMap, $attrsToConversion, $attributesFromIdP): array |
| 184 | +{ |
73 | 185 | $attributesToUpdate = []; |
74 | 186 |
|
75 | 187 | foreach ($attributesFromPerun as $attribute) { |
76 | | - $attrName = $attribute['name']; |
| 188 | + $attrName = $attribute[NAME]; |
77 | 189 |
|
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; |
80 | 193 |
|
| 194 | + if (null !== $mappedAttributeName && null !== $idpAttribute) { |
81 | 195 | 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); |
90 | 197 | } |
91 | 198 |
|
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; |
98 | 203 | } else { |
99 | | - throw new Exception('perun/www/updateUes.php: unsupported type of attribute.'); |
| 204 | + throw new Exception(CLASS_PREFIX . 'Unsupported type of attribute.'); |
100 | 205 | } |
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; |
105 | 211 | } |
106 | 212 | } |
107 | 213 | } |
108 | 214 |
|
| 215 | + return $attributesToUpdate; |
| 216 | +} |
| 217 | + |
| 218 | +function updateUserExtSource($adapter, $userExtSource, $attributesToUpdate): bool |
| 219 | +{ |
109 | 220 | $attributesToUpdateFinal = []; |
| 221 | + |
110 | 222 | if (!empty($attributesToUpdate)) { |
111 | 223 | 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; |
114 | 226 | } |
115 | | - $adapter->setUserExtSourceAttributes($userExtSource['id'], $attributesToUpdateFinal); |
| 227 | + |
| 228 | + $adapter->setUserExtSourceAttributes($userExtSource[ID], $attributesToUpdateFinal); |
116 | 229 | } |
117 | 230 |
|
118 | | - $adapter->updateUserExtSourceLastAccess($userExtSource['id']); |
| 231 | + $adapter->updateUserExtSourceLastAccess($userExtSource[ID]); |
119 | 232 |
|
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; |
126 | 262 | } |
0 commit comments