connect_errno) die("Could not connect to db: " . $db->connect_error . "\n"); $db->set_charset("utf8mb4"); if ($localFile !== false && file_exists($localFile)) { $data = file_get_contents($localFile); } else { pdebug("Downloading..."); $ch = curl_init(); if ($ch === false) die("Could not init curl\n"); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $data = curl_exec($ch); if ($data === false) { die("Could not download DFN-AAI meta data\n"); } } function getAttributes($array, $path) { if (!is_array($path)) { // Convert '/path/syntax/foo/wanteditem' to array for further processing and recursive calls $path = explode('/', $path); } do { // Get next element from array, loop to ignore empty elements (so double slashes in the path are allowed) $element = array_shift($path); } while (empty($element) && !empty($path)); if (!isset($array[$element])) { // Current path element does not exist - error return array(); } if (empty($path)) { // Path is now empty which means we're at 'wanteditem' from out example above now if (!is_array($array[$element]) || !isset($array[$element][0])) { // If it's a leaf node of the array, wrap it in plain array, so the function will // always return an array on success return array($array[$element]); } // 'wanteditem' is not a unique leaf node, return as is // This means it's either a plain array, in case there are multiple 'wanteditem' elements on the same level // or it's an associative array if 'wanteditem' has any sub-nodes return $array[$element]; } // Recurse if (!is_array($array[$element])) { // We're in the middle of the requested path, but the current element is already a leaf node with no // children - error return array(); } if (isset($array[$element][0])) { // The currently handled element of the path exists multiple times on the current level, so it is // wrapped in a plain array - recurse into each one of them and merge the results $return = array(); foreach ($array[$element] as $item) { $return = array_merge($return, getAttributes($item, $path)); } return $return; } // Unique non-leaf node - simple recursion return getAttributes($array[$element], $path); } // Safety check: If XML module is missing, don't wipe DB @simplexml_load_string(''); // Also, only wipe old URLs if we found at least one valid URL function wipeDb() { global $db; static $wiped = false; if ($wiped) return; $wiped = true; $db->query("LOCK TABLES organization WRITE, organization_suffix WRITE"); $db->query("UPDATE organization SET authmethod = '' WHERE authmethod LIKE 'http%'"); } // Regular ECP auth for suite $data = preg_replace('#<(/?)[a-zA-Z0-9_-]+:#', '<\1', $data); $data = preg_replace('# ([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)=#', ' \1_\2=', $data); $count = preg_match_all('##s', $data, $out); unset($data); pdebug("Found $count EntityDescriptors"); foreach ($out[0] as $data) { $xml = json_decode(json_encode(simplexml_load_string('' . $data )), true); $name = getAttributes($xml, "IDPSSODescriptor/Extensions/UIInfo/DisplayName"); if (is_array($name) && !empty($name)) { $name = $name[0]; pdebug(" *** Found $name"); } else { pdebug(" *** Entry without DisplayName..."); continue; } if ($requiredAttribute !== false && !in_array($requiredAttribute, getAttributes($xml, 'Extensions/EntityAttributes/Attribute/AttributeValue'))) { pdebug("Not bwIDM member..."); continue; } $scope = getAttributes($xml, "IDPSSODescriptor/Extensions/Scope"); if (empty($scope)) { pdebug("No list of scopes..."); continue; } $ecp = false; foreach (getAttributes($xml, "IDPSSODescriptor/SingleSignOnService") as $sso) { if (isset($sso['@attributes']['Binding']) && $sso['@attributes']['Binding'] === 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP') { $ecp = $sso['@attributes']['Location']; break; } } // Now usable: $scope, $name, $ecp (if known, false otherwise) if ($requireEcp && $ecp === false) { pdebug("No ECP end-point..."); } else { pdebug("Adding/Updating with ECP URL $ecp, suffixes: " . implode(', ', $scope)); wipeDb(); $orgid = false; // Try to use any existing organization ID based on the suffixes. This is to avoid adding the same organzation twice, should the first entry in the list change foreach ($scope as $alias) { $ealias = $db->escape_string($alias); $res = $db->query("SELECT organizationid FROM organization_suffix WHERE suffix = '$ealias' LIMIT 1"); if ($row = $res->fetch_assoc()) { $orgid = $row['organizationid']; break; } } if ($orgid === false) { // Not known yet, use first $orgid = $scope[0]; } $eid = $db->escape_string($orgid); $ename = $db->escape_string($name); $eecp = $db->escape_string($ecp); $db->query("INSERT INTO organization (organizationid, name, authmethod, publickey) VALUES ('$eid', '$ename', '$eecp', '') ON DUPLICATE KEY UPDATE authmethod = VALUES(authmethod), name = VALUES(name)"); foreach ($scope as $alias) { $ealias = $db->escape_string($alias); $db->query("INSERT IGNORE INTO organization_suffix (organizationid, suffix) VALUES ('$eid', '$ealias')"); } } } $db->query("UNLOCK TABLES"); // Mapping of suffix to idp (and back) if (is_array($suffixMappings)) { $db->query("LOCK TABLES suffix2idp WRITE"); $db->query("TRUNCATE TABLE suffix2idp"); foreach ($suffixMappings as $file) { $data = file_get_contents($file); if (empty($data)) continue; $data = preg_replace('#<(/?)[a-zA-Z0-9_-]+:#', '<\1', $data); $data = preg_replace('# ([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)=#', ' \1_\2=', $data); $count = preg_match_all('##s', $data, $out); unset($data); pdebug("Found $count EntityDescriptors"); foreach ($out[0] as $data) { $xml = json_decode(json_encode(simplexml_load_string('' . $data )), true); $scope = getAttributes($xml, "IDPSSODescriptor/Extensions/Scope"); if (empty($scope)) { pdebug("No list of scopes..."); continue; } $id = getAttributes($xml, "@attributes/entityID"); if (is_array($id) && !empty($id)) { $id = $id[0]; } $ereg = ''; $reg = getAttributes($xml, "Extensions/RegistrationInfo/@attributes/registrationAuthority"); if (is_array($reg) && !empty($reg)) { $ereg = $db->escape_string($reg[0]); } $eid = $db->escape_string($id); foreach ($scope as $alias) { $ealias = $db->escape_string($alias); $db->query("INSERT IGNORE INTO suffix2idp (idpurl, suffix, regauth)" . " VALUES ('$eid', '$ealias', '$ereg')"); } } } $db->query("UNLOCK TABLES"); }