readMetadata.php 23.9 KB
Newer Older
Lukas Haemmerle's avatar
Lukas Haemmerle committed
1
<?php // Copyright (c) 2017, SWITCH
2

haemmer's avatar
haemmer committed
3
// This file is used to dynamically create the list of IdPs and SP to be 
4 5
// displayed for the WAYF/DS service based on the federation metadata.
// Configuration parameters are specified in config.php.
haemmer's avatar
haemmer committed
6 7 8 9
//
// The list of Identity Providers can also be updated by running the script
// readMetadata.php periodically as web server user, e.g. with a cron entry like:
// 5 * * * * /usr/bin/php readMetadata.php > /dev/null
haemmer's avatar
haemmer committed
10

11 12 13 14 15 16

// Set dummy server name if run in CLI
if (!isset($_SERVER['SERVER_NAME'])){
	$_SERVER['SERVER_NAME'] = 'localhost';
}

haemmer's avatar
haemmer committed
17 18 19
require_once('functions.php');
require_once('config.php');

haemmer's avatar
haemmer committed
20
// Make sure this script is not accessed directly
21
if(isRunViaCLI()){
haemmer's avatar
haemmer committed
22
	// Run in cli mode.
23
	
24 25 26 27
	// Set default config options
	initConfigOptions();
	
	// Load Identity Providers
haemmer's avatar
haemmer committed
28 29
	require($IDPConfigFile);
	
haemmer's avatar
haemmer committed
30 31 32 33 34
	// Check that $IDProviders exists
	if (!isset($IDProviders) or !is_array($IDProviders)){
		$IDProviders = array();
	}
	
haemmer's avatar
haemmer committed
35
	if (
36
		   !file_exists($metadataFile) 
haemmer's avatar
haemmer committed
37 38 39
		|| trim(@file_get_contents($metadataFile)) == '') {
	  exit ("Exiting: File ".$metadataFile." is empty or does not exist\n");
	}
40
	
41 42 43 44 45 46 47 48 49 50
	// Get an exclusive lock to generate our parsed IdP and SP files.
	if (($lockFp = fopen($metadataLockFile, 'a+')) === false) {
		$errorMsg = 'Could not open lock file '.$metadataLockFile;
		die($errorMsg);
	}
	if (flock($lockFp, LOCK_EX) === false) { 
		$errorMsg = 'Could not lock file '.$metadataLockFile;
		die($errorMsg);
	}
	
51 52
	echo 'Parsing metadata file '.$metadataFile."\n";
	list($metadataIDProviders, $metadataSProviders) = parseMetadata($metadataFile, $defaultLanguage);
haemmer's avatar
haemmer committed
53
	
54
	// If $metadataIDProviders is not FALSE, dump results in $metadataIDPFile.
haemmer's avatar
haemmer committed
55 56
	if(is_array($metadataIDProviders)){ 
		
57 58
		echo 'Dumping parsed Identity Providers to file '.$metadataIDPFile."\n";
		dumpFile($metadataIDPFile, $metadataIDProviders, 'metadataIDProviders');
59 60 61
	}
	// If $metadataSProviders is not FALSE, dump results in $metadataSPFile.
	if(is_array($metadataSProviders)){ 
haemmer's avatar
haemmer committed
62
		
63 64 65 66 67 68 69
		echo 'Dumping parsed Service Providers to file '.$metadataSPFile."\n";
		dumpFile($metadataSPFile, $metadataSProviders, 'metadataSProviders');
	}

	// Release the lock, and close.
	flock($lockFp, LOCK_UN);
	fclose($lockFp);
haemmer's avatar
haemmer committed
70 71
	
	
72 73 74
	// If $metadataIDProviders is not FALSE, update $IDProviders and print the Identity Providers lists.
	if(is_array($metadataIDProviders)){ 

75
		echo 'Merging parsed Identity Providers with data from file '.$IDPConfigFile."\n";
haemmer's avatar
haemmer committed
76 77 78
		$IDProviders = mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries);
		
		echo "Printing parsed Identity Providers:\n";
79 80 81
		print_r($metadataIDProviders);
		
		echo "Printing effective Identity Providers:\n";
haemmer's avatar
haemmer committed
82 83 84
		print_r($IDProviders);
	}
	
85
	// If $metadataSProviders is not FALSE, update $SProviders and print the list.
86 87
	if(is_array($metadataSProviders)){ 
		
haemmer's avatar
haemmer committed
88
		// For now copy the array by reference
89 90 91 92 93 94
		$SProviders = &$metadataSProviders;
		
		echo "Printing parsed Service Providers:\n";
		print_r($metadataSProviders);
	}
	
haemmer's avatar
haemmer committed
95
	
96
} elseif (isRunViaInclude()) {
haemmer's avatar
haemmer committed
97
	// Run as included file
98
	
99 100 101
	// Open the metadata lock file.
	if (($lockFp = fopen($metadataLockFile, 'a+')) === false) {
		$errorMsg = 'Could not open lock file '.$metadataLockFile;
haemmer's avatar
haemmer committed
102
		logError($errorMsg);
103
	}
haemmer's avatar
haemmer committed
104 105 106 107 108 109
	
	// Check that $IDProviders exists
	if (!isset($IDProviders) or !is_array($IDProviders)){
		$IDProviders = array();
	}
	
110
	// Run as included file
haemmer's avatar
haemmer committed
111
	if(!file_exists($metadataIDPFile) or filemtime($metadataFile) > filemtime($metadataIDPFile)){
haemmer's avatar
haemmer committed
112
	
113 114 115 116
		// Get an exclusive lock to regenerate the parsed files.
		if ($lockFp !== false) {
			if (flock($lockFp, LOCK_EX) === false) { 
				$errorMsg = 'Could not get exclusive lock on '.$metadataLockFile;
haemmer's avatar
haemmer committed
117
				logError($errorMsg);
118 119
			}
		}
haemmer's avatar
haemmer committed
120 121 122
	}
	
	// Now that we have the lock, check again
haemmer's avatar
haemmer committed
123 124 125 126
	if(
		(!file_exists($metadataIDPFile) or filemtime($metadataFile) > filemtime($metadataIDPFile)) 
		and regenerateMetadata($metadataFile, $defaultLanguage)
	){
haemmer's avatar
haemmer committed
127 128
		
		// Now merge IDPs from metadata and static file
129 130
		$IDProviders = mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries);
		
haemmer's avatar
haemmer committed
131
		// For now copy the array by reference
132 133
		$SProviders = &$metadataSProviders;
		
haemmer's avatar
haemmer committed
134 135
	} elseif (file_exists($metadataIDPFile)){
		
136 137 138
		// Get a shared lock to read the IdP and SP files
		// generated from the metadata file.
		if ($lockFp !== false) {
haemmer's avatar
haemmer committed
139 140 141 142 143
			
			// Release the lock in case we had it for some 
			// reason and still ended up here
			flock($lockFp, LOCK_UN);
			
144 145
			if (flock($lockFp, LOCK_SH) === false) { 
				$errorMsg = 'Could not lock file '.$metadataLockFile;
haemmer's avatar
haemmer committed
146
				logError($errorMsg);
147 148
			}
		}
haemmer's avatar
haemmer committed
149
		
150
		// Read SP and IDP files generated with metadata
haemmer's avatar
haemmer committed
151
		require($metadataIDPFile);
152
		require($metadataSPFile);
haemmer's avatar
haemmer committed
153
	
154 155 156 157
		// Release the lock.
		if ($lockFp !== false) {
			flock($lockFp, LOCK_UN);
		}
haemmer's avatar
haemmer committed
158
		
159 160 161
		// Now merge IDPs from metadata and static file
		$IDProviders = mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries);
		
haemmer's avatar
haemmer committed
162
		// For now copy the array by reference
163 164
		$SProviders = &$metadataSProviders;
	}
haemmer's avatar
haemmer committed
165
	
166 167 168 169
	// Close the metadata lock file.
	if ($lockFp !== false) {
		fclose($lockFp);
	}
170
	
haemmer's avatar
haemmer committed
171 172 173 174
} else {
	exit('No direct script access allowed');
}

haemmer's avatar
haemmer committed
175 176
closelog();

haemmer's avatar
haemmer committed
177
/*****************************************************************************/
178 179
// Function parseMetadata, parses metadata file and returns Array($IdPs, SPs)  or
// Array(false, false) if error occurs while parsing metadata file
haemmer's avatar
haemmer committed
180
function parseMetadata($metadataFile, $defaultLanguage){
181
	global $supportHideFromDiscoveryEntityCategory;
haemmer's avatar
haemmer committed
182
	
183
	if(!file_exists($metadataFile)){
haemmer's avatar
haemmer committed
184
		$errorMsg = 'File '.$metadataFile." does not exist"; 
185
		if (isRunViaCLI()){
haemmer's avatar
haemmer committed
186
			echo $errorMsg."\n";
187
		} else {
haemmer's avatar
haemmer committed
188
			logError($errorMsg);
189 190 191 192 193
		}
		return Array(false, false);
	}

	if(!is_readable($metadataFile)){
haemmer's avatar
haemmer committed
194 195 196 197
		$errorMsg = 'File '.$metadataFile." cannot be read due to insufficient permissions"; 
		if (isRunViaCLI()){
			echo $errorMsg."\n";
		} else {
haemmer's avatar
haemmer committed
198
			logError($errorMsg);
haemmer's avatar
haemmer committed
199
		}
200 201
		return Array(false, false);
	}
haemmer's avatar
haemmer committed
202
	
haemmer's avatar
haemmer committed
203 204
	$CurrentXMLReaderNode = new XMLReader();
	if(!$CurrentXMLReaderNode->open($metadataFile, null, LIBXML_PARSEHUGE | LIBXML_NOERROR | LIBXML_NOWARNING | 1)){
haemmer's avatar
haemmer committed
205 206 207 208
		$errorMsg = 'Could not parse metadata file '.$metadataFile; 
		if (isRunViaCLI()){
			echo $errorMsg."\n";
		} else {
haemmer's avatar
haemmer committed
209
			logError($errorMsg);
haemmer's avatar
haemmer committed
210
		}
211 212 213
		return Array(false, false);
	}
	
haemmer's avatar
haemmer committed
214 215 216 217 218 219 220 221 222 223 224 225 226
	// Go to first element and check it is named 'EntitiesDescriptor'
	// If not it's probably not a valid SAML metadata file
	$CurrentXMLReaderNode->read();
	if ($CurrentXMLReaderNode->localName  !== 'EntitiesDescriptor') {
		$errorMsg = 'Metadata file '.$metadataFile.' does not include a root node EntitiesDescriptor'; 
		if (isRunViaCLI()){
			echo $errorMsg."\n";
		} else {
			logError($errorMsg);
		}
		return Array(false, false);
	}
	
Lukas Haemmerle's avatar
Lukas Haemmerle committed
227
	// Init variables
228
	$hiddenIdPs = 0;
Lukas Haemmerle's avatar
Lukas Haemmerle committed
229 230
	$metadataIDProviders = array();
	$metadataSProviders = array();
231
	
haemmer's avatar
haemmer committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	// Process individual EntityDescriptors
	while( $CurrentXMLReaderNode->read() ) {
		if($CurrentXMLReaderNode->nodeType == XMLReader::ELEMENT && $CurrentXMLReaderNode->localName  === 'EntityDescriptor') {
			$entityID = $CurrentXMLReaderNode->getAttribute('entityID');
			$EntityDescriptorXML = $CurrentXMLReaderNode->readOuterXML();
			$EntityDescriptorDOM = new DOMDocument();
			$EntityDescriptorDOM->loadXML($EntityDescriptorXML);
			
			// Check role descriptors
			foreach($EntityDescriptorDOM->documentElement->childNodes as $RoleDescriptor) {
				$nodeName = $RoleDescriptor->localName;
				switch($nodeName){
					case 'IDPSSODescriptor':
						$IDP = processIDPRoleDescriptor($RoleDescriptor);
						if ($IDP){
							$metadataIDProviders[$entityID] = $IDP;
248 249
						} else {
							$hiddenIdPs++;
haemmer's avatar
haemmer committed
250 251 252 253 254 255
						}
						break;
					case 'SPSSODescriptor':
						$SP = processSPRoleDescriptor($RoleDescriptor);
						if ($SP){
							$metadataSProviders[$entityID] = $SP;
haemmer's avatar
haemmer committed
256
						} else {
haemmer's avatar
haemmer committed
257 258 259 260 261 262
							$errorMsg = "Failed to load SP with entityID $entityID from metadata file $metadataFile";
							if (isRunViaCLI()){
								echo $errorMsg."\n";
							} else {
								logWarning($errorMsg);
							}
haemmer's avatar
haemmer committed
263
						}
haemmer's avatar
haemmer committed
264 265 266
						break;
					default:
				}
haemmer's avatar
haemmer committed
267 268 269 270
			}
		}
	}
	
haemmer's avatar
haemmer committed
271
	// Output result
272 273 274 275
	$infoMsg = "Successfully parsed metadata file ".$metadataFile. " ";
	$infoMsg .= "(".count($metadataIDProviders)." IdPs, ";
	$infoMsg .= " ".count($metadataSProviders)." SPs, ";
	$infoMsg .=  ($hiddenIdPs > 0) ? $hiddenIdPs." IdPs are hidden)" : "no hidden IdPs)" ;
276
	
haemmer's avatar
haemmer committed
277 278 279
	if (isRunViaCLI()){
		echo $infoMsg."\n";
	} else {
haemmer's avatar
haemmer committed
280
		logInfo($infoMsg);
haemmer's avatar
haemmer committed
281 282 283
	}
	
	
284
	return Array($metadataIDProviders, $metadataSProviders);
haemmer's avatar
haemmer committed
285 286
}

haemmer's avatar
haemmer committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

/******************************************************************************/
// Load SAML metadata file, parse it and update 
// IDProvider.metadata.php and SProvider.metadata.php files
function regenerateMetadata($metadataFile, $defaultLanguage) {
	global $metadataIDPFile, $metadataSPFile, $IDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries, $lockFp;
	
	// Regenerate $metadataIDPFile.
	list($metadataIDProviders, $metadataSProviders) = parseMetadata($metadataFile, $defaultLanguage);
	
	if($metadataIDProviders == false) {
		return false;
	}
	
	// If $metadataIDProviders is not an array (parse error in metadata),
	// $IDProviders from $IDPConfigFile will be used.
	if(is_array($metadataIDProviders)){
		dumpFile($metadataIDPFile, $metadataIDProviders, 'metadataIDProviders');
		$IDProviders = mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries);
	}
	
	if(is_array($metadataSProviders)){
		dumpFile($metadataSPFile, $metadataSProviders, 'metadataSProviders');
		require($metadataSPFile);
	}
	
	// Release the lock.
	if ($lockFp !== false) {
		flock($lockFp, LOCK_UN);
	}
	
}

320 321 322 323
/******************************************************************************/
// Processes an IDPRoleDescriptor XML node and returns an IDP entry or false if 
// something went wrong
function processIDPRoleDescriptor($IDPRoleDescriptorNode){
324
	global $defaultLanguage, $supportHideFromDiscoveryEntityCategory;
haemmer's avatar
haemmer committed
325
	
326
	$IDP = Array();
haemmer's avatar
haemmer committed
327
	$Profiles = Array();
328 329 330 331 332 333 334 335 336
	
	// Skip Idp if it has the Hide-From-Discovery entity 
	// category attribute
	if (!isset($supportHideFromDiscoveryEntityCategory) || $supportHideFromDiscoveryEntityCategory){
		if (hasHideFromDiscoveryEntityCategory($IDPRoleDescriptorNode)){
			return false;
		}
	}
	
337 338 339
	// Get SSO URL
	$SSOServices = $IDPRoleDescriptorNode->getElementsByTagNameNS( 'urn:oasis:names:tc:SAML:2.0:metadata', 'SingleSignOnService' );
	foreach( $SSOServices as $SSOService ){
haemmer's avatar
haemmer committed
340
	  $Profiles[$SSOService->getAttribute('Binding')] = $SSOService->getAttribute('Location');
341 342
	}
	
haemmer's avatar
haemmer committed
343
	// Set SAML1 SSO URL
haemmer's avatar
haemmer committed
344
	if (isset($Profiles['urn:mace:shibboleth:1.0:profiles:AuthnRequest'])) {
haemmer's avatar
haemmer committed
345
		$IDP['SSO'] = $Profiles['urn:mace:shibboleth:1.0:profiles:AuthnRequest'];
haemmer's avatar
haemmer committed
346
	} else if (isset($Profiles['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'])) {
haemmer's avatar
haemmer committed
347 348
		$IDP['SSO'] = $Profiles['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'];
	} else {
349
		$IDP['SSO'] = 'https://no.saml1.or.saml2.sso.url.defined.com/error';
350 351
	}
	
352 353 354 355 356 357 358 359
	// First get MDUI name
	$MDUIDisplayNames = getMDUIDisplayNames($IDPRoleDescriptorNode);
	if (count($MDUIDisplayNames)){
		$IDP['Name'] = current($MDUIDisplayNames);
	}
	foreach ($MDUIDisplayNames as $lang => $value){
		$IDP[$lang]['Name'] = $value;
	}
360
	
361 362 363 364
	// Then try organization names 
	if (empty($IDP['Name'])){
		$OrgnizationNames = getOrganizationNames($IDPRoleDescriptorNode);
		$IDP['Name'] = current($OrgnizationNames);
haemmer's avatar
haemmer committed
365
		
366 367
		foreach ($OrgnizationNames as $lang => $value){
			$IDP[$lang]['Name'] = $value;
368
		}
369 370 371 372
	} 
	
	// As last resort, use entityID
	if (empty($IDP['Name'])){
373 374 375
		$IDP['Name'] = $IDPRoleDescriptorNode->parentNode->getAttribute('entityID');
	}
	
376 377 378 379 380 381 382 383 384 385 386
	// Set default name
	if (isset($IDP[$defaultLanguage])){
		$IDP['Name'] = $IDP[$defaultLanguage]['Name'];
	} elseif (isset($IDP['en'])){
		$IDP['Name'] = $IDP['en']['Name'];
	}
	
	// Get supported protocols
	$protocols = $IDPRoleDescriptorNode->getAttribute('protocolSupportEnumeration');
	$IDP['Protocols'] = $protocols;
	
387 388 389 390 391 392
	// Get keywords
	$MDUIKeywords = getMDUIKeywords($IDPRoleDescriptorNode);
	foreach ($MDUIKeywords as $lang => $keywords){
		$IDP[$lang]['Keywords'] = $keywords;
	}
	
haemmer's avatar
haemmer committed
393 394 395 396
	// Get Logos
	$MDUILogos = getMDUILogos($IDPRoleDescriptorNode);
	foreach ($MDUILogos as $Logo){
		// Skip non-favicon logos
haemmer's avatar
haemmer committed
397
		if ($Logo['Height'] != 16 || $Logo['Width'] != 16 ){
haemmer's avatar
haemmer committed
398 399 400
			continue;
		}
		
haemmer's avatar
haemmer committed
401 402 403 404 405 406
		// Strip height and width
		unset($Logo['Height']);
		unset($Logo['Width']);
		
		if ($Logo['Lang'] == ''){
			unset($Logo['Lang']);
haemmer's avatar
haemmer committed
407 408
			$IDP['Logo'] = $Logo;
		} else {
haemmer's avatar
haemmer committed
409 410
			$lang = $Logo['Lang'];
			unset($Logo['Lang']);
haemmer's avatar
haemmer committed
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
			$IDP[$lang]['Logo'] = $Logo;
		}
	}
	
	// Get AttributeValue 
	$SAMLAttributeValues = getSAMLAttributeValues($IDPRoleDescriptorNode);
	if ($SAMLAttributeValues){
		$IDP['AttributeValue'] = $SAMLAttributeValues;
	}
	
	// Get IPHints 
	$MDUIIPHints = getMDUIIPHints($IDPRoleDescriptorNode);
	if ($MDUIIPHints){
		$IDP['IPHint'] = $MDUIIPHints;
	}
	
	// Get DomainHints 
	$MDUIDomainHints = getMDUIDomainHints($IDPRoleDescriptorNode);
	if ($MDUIDomainHints){
		$IDP['DomainHint'] = $MDUIDomainHints;
	}
	
	// Get GeolocationHints 
	$MDUIGeolocationHints = getMDUIGeolocationHints($IDPRoleDescriptorNode);
	if ($MDUIGeolocationHints){
		$IDP['GeolocationHint'] = $MDUIGeolocationHints;
	}
	
439 440 441 442 443 444 445
	return $IDP;
}

/******************************************************************************/
// Processes an SPRoleDescriptor XML node and returns an SP entry or false if 
// something went wrong
function processSPRoleDescriptor($SPRoleDescriptorNode){
446 447
	global $defaultLanguage;

448 449 450 451 452 453 454 455 456 457
	$SP = Array();
	
	// Get <idpdisc:DiscoveryResponse> extensions
	$DResponses = $SPRoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol', 'DiscoveryResponse');
	foreach( $DResponses as $DResponse ){
		if ($DResponse->getAttribute('Binding') == 'urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol'){
			$SP['DSURL'][] =  $DResponse->getAttribute('Location');
		}
	}
	
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
	// First get MDUI name
	$MDUIDisplayNames = getMDUIDisplayNames($SPRoleDescriptorNode);
	if (count($MDUIDisplayNames)){
		$SP['Name'] = current($MDUIDisplayNames);
	}
	foreach ($MDUIDisplayNames as $lang => $value){
		$SP[$lang]['Name'] = $value;
	}
	
	// Then try attribute consuming service
	if (empty($SP['Name'])){
		$ConsumingServiceNames = getAttributeConsumingServiceNames($SPRoleDescriptorNode);
		$SP['Name'] = current($ConsumingServiceNames);
		
		foreach ($ConsumingServiceNames as $lang => $value){
			$SP[$lang]['Name'] = $value;
		}
	} 
	
	// As last resort, use entityID
	if (empty($SP['Name'])){
		$SP['Name'] = $SPRoleDescriptorNode->parentNode->getAttribute('entityID');
	}
	
	// Set default name
	if (isset($SP[$defaultLanguage])){
		$SP['Name'] = $SP[$defaultLanguage]['Name'];
	} elseif (isset($SP['en'])){
		$SP['Name'] = $SP['en']['Name'];
	}
	
489 490 491 492 493 494
	// Get Assertion Consumer Services and store their hostnames
	$ACServices = $SPRoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:metadata', 'AssertionConsumerService');
	foreach( $ACServices as $ACService ){
		$SP['ACURL'][] =  $ACService->getAttribute('Location');
	}
	
495 496 497 498
	// Get supported protocols
	$protocols = $SPRoleDescriptorNode->getAttribute('protocolSupportEnumeration');
	$SP['Protocols'] = $protocols;
	
499 500 501 502 503 504
	// Get keywords
	$MDUIKeywords = getMDUIKeywords($SPRoleDescriptorNode);
	foreach ($MDUIKeywords as $lang => $keywords){
		$SP[$lang]['Keywords'] = $keywords;
	}
	
505 506 507 508 509 510 511 512
	return $SP;
}

/******************************************************************************/
// Dump variable to a file 
function dumpFile($dumpFile, $providers, $variableName){
	 
	if(($fp = fopen($dumpFile, 'w')) !== false){
513 514 515
		fwrite($fp, "<?php\n\n");
		fwrite($fp, "// This file was automatically generated by readMetadata.php\n");
		fwrite($fp, "// Don't edit!\n\n");
haemmer's avatar
haemmer committed
516
		
517 518 519 520 521
		fwrite($fp, '$'.$variableName.' = ');
		fwrite($fp, var_export($providers,true));
		
		fwrite($fp, "\n?>");
			
522
		fclose($fp);
haemmer's avatar
haemmer committed
523
	} else {
haemmer's avatar
haemmer committed
524 525 526 527
		$errorMsg = 'Could not open file '.$dumpFile.' for writting';
		if (isRunViaCLI()){
			echo $errorMsg."\n";
		} else {
haemmer's avatar
haemmer committed
528
			logInfo($errorMsg);
haemmer's avatar
haemmer committed
529
		}
haemmer's avatar
haemmer committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543
	}
}


/******************************************************************************/
// Function mergeInfo is used to create the effective $IDProviders array.
// For each IDP found in the metadata, merge the values from IDProvider.conf.php.
// If an IDP is found in IDProvider.conf as well as in metadata, use metadata  
// information if $SAML2MetaOverLocalConf is true or else use IDProvider.conf data
function mergeInfo($IDProviders, $metadataIDProviders, $SAML2MetaOverLocalConf, $includeLocalConfEntries){

	// If $includeLocalConfEntries parameter is set to true, mergeInfo() will also consider IDPs
	// not listed in metadataIDProviders but defined in IDProviders file
	// This is required if you need to add local exceptions over the federation metadata
544 545
	$allIDPS = $metadataIDProviders;
	$mergedArray = Array();
haemmer's avatar
haemmer committed
546
	if ($includeLocalConfEntries) {
haemmer's avatar
haemmer committed
547
		$allIDPS = array_merge($metadataIDProviders, $IDProviders);
haemmer's avatar
haemmer committed
548
	}
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
	
	foreach ($allIDPS as $allIDPsKey => $allIDPsEntry){
		if(isset($IDProviders[$allIDPsKey])){
			// Entry exists also in local IDProviders.conf.php
			if (isset($metadataIDProviders[$allIDPsKey]) && is_array($metadataIDProviders[$allIDPsKey])) {
				
				// Remove IdP if there is a removal rule in local IDProviders.conf.php 
				if (!is_array($IDProviders[$allIDPsKey])){
					unset($metadataIDProviders[$allIDPsKey]);
					continue;
				}
				
				// Entry exists in both IDProviders sources and is an array
				if($SAML2MetaOverLocalConf){
					// Metadata entry overwrite local conf
					$mergedArray[$allIDPsKey] = array_merge($IDProviders[$allIDPsKey], $metadataIDProviders[$allIDPsKey]);
				} else {
					// Local conf overwrites metada entry
					$mergedArray[$allIDPsKey] = array_merge($metadataIDProviders[$allIDPsKey], $IDProviders[$allIDPsKey]);
				}
haemmer's avatar
haemmer committed
569
			} else {
570 571
					// Entry only exists in local IDProviders file
					$mergedArray[$allIDPsKey] = $IDProviders[$allIDPsKey];
haemmer's avatar
haemmer committed
572
			}
haemmer's avatar
haemmer committed
573
		} else {
574 575
			// Entry doesnt exist in in local IDProviders.conf.php
			$mergedArray[$allIDPsKey] = $metadataIDProviders[$allIDPsKey];
haemmer's avatar
haemmer committed
576 577 578 579 580 581
		}
	}
	
	return $mergedArray;
}

582 583 584 585 586 587 588 589 590
/******************************************************************************/
// Get MD Display Names from RoleDescriptor
function getMDUIDisplayNames($RoleDescriptorNode){
	
	$Entity = Array();
	
	$MDUIDisplayNames = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'DisplayName');
	foreach( $MDUIDisplayNames as $MDUIDisplayName ){
		$lang = $MDUIDisplayName->getAttributeNodeNS('http://www.w3.org/XML/1998/namespace', 'lang')->nodeValue;
haemmer's avatar
haemmer committed
591
		$Entity[$lang] = trimToSingleLine($MDUIDisplayName->nodeValue);
592 593 594 595 596
	}
	
	return $Entity;
}

597 598 599 600 601 602 603 604 605
/******************************************************************************/
// Get MD Keywords from RoleDescriptor
function getMDUIKeywords($RoleDescriptorNode){
	
	$Entity = Array();
	
	$MDUIKeywords = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'Keywords');
	foreach( $MDUIKeywords as $MDUIKeywordEntry ){
		$lang = $MDUIKeywordEntry->getAttributeNodeNS('http://www.w3.org/XML/1998/namespace', 'lang')->nodeValue;
haemmer's avatar
haemmer committed
606
		$Entity[$lang] = trimToSingleLine($MDUIKeywordEntry->nodeValue);
607 608 609 610
	}
	
	return $Entity;
}
haemmer's avatar
haemmer committed
611 612 613 614 615 616 617 618 619

/******************************************************************************/
// Get MD Logos from RoleDescriptor. Prefer the favicon logos
function getMDUILogos($RoleDescriptorNode){
	
	$Logos = Array();
	$MDUILogos = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'Logo');
	foreach( $MDUILogos as $MDUILogoEntry ){
		$Logo = Array();
haemmer's avatar
haemmer committed
620 621 622 623
		$Logo['URL'] = trimToSingleLine($MDUILogoEntry->nodeValue);
		$Logo['Height'] = ($MDUILogoEntry->getAttribute('height') != '') ? trimToSingleLine($MDUILogoEntry->getAttribute('height')) : '16';
		$Logo['Width'] = ($MDUILogoEntry->getAttribute('width') != '') ? trimToSingleLine($MDUILogoEntry->getAttribute('width')) : '16';
		$Logo['Lang'] = ($MDUILogoEntry->getAttribute('lang') != '') ? trimToSingleLine($MDUILogoEntry->getAttribute('lang')) : '';
haemmer's avatar
haemmer committed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
		$Logos[] = $Logo;
	}
	
	return $Logos;
}


/******************************************************************************/
// Get MD Attribute Value(kind) from RoleDescriptor
function getSAMLAttributeValues($RoleDescriptorNode){
	
	$Entity = Array();
	
	$SAMLAttributeValues = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'AttributeValue');
	foreach( $SAMLAttributeValues as $SAMLAttributeValuesEntry ){
haemmer's avatar
haemmer committed
639
		$Entity[] = trimToSingleLine($SAMLAttributeValuesEntry->nodeValue);
haemmer's avatar
haemmer committed
640 641 642 643 644 645 646 647 648 649 650 651 652 653
	}
	
	return $Entity;
}


/******************************************************************************/
// Get MD IP Address Hints from RoleDescriptor
function getMDUIIPHints($RoleDescriptorNode){
	
	$Entity = Array();
	
	$MDUIIPHints = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'IPHint');
	foreach( $MDUIIPHints as $MDUIIPHintEntry ){
haemmer's avatar
haemmer committed
654 655 656 657
		if (preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/", trimToSingleLine($MDUIIPHintEntry->nodeValue), $splitIP)){
			$Entity[] = trimToSingleLine($splitIP[0]);
		} elseif (preg_match("/^.*\:.*\/[0-9]{1,2}$/", trimToSingleLine($MDUIIPHintEntry->nodeValue), $splitIP)){ 
			$Entity[] = trimToSingleLine($splitIP[0]);
haemmer's avatar
haemmer committed
658 659 660 661 662 663 664 665 666 667 668 669 670 671
		}
	}
	
	return $Entity;
}

/******************************************************************************/
// Get MD Domain Hints from RoleDescriptor
function getMDUIDomainHints($RoleDescriptorNode){
	
	$Entity = Array();
	
	$MDUIDomainHints = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'DomainHint');
	foreach( $MDUIDomainHints as $MDUIDomainHintEntry ){
haemmer's avatar
haemmer committed
672
		$Entity[] = trimToSingleLine($MDUIDomainHintEntry->nodeValue);
haemmer's avatar
haemmer committed
673 674 675 676 677 678 679 680 681 682 683 684 685
	}
	
	return $Entity;
}

/******************************************************************************/
// Get MD Geolocation Hints from RoleDescriptor
function getMDUIGeolocationHints($RoleDescriptorNode){
	
	$Entity = Array();
	
	$MDUIGeolocationHints = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:metadata:ui', 'GeolocationHint');
	foreach( $MDUIGeolocationHints as $MDUIGeolocationHintEntry ){
haemmer's avatar
haemmer committed
686 687
		if (preg_match("/^geo:([0-9]+\.{0,1}[0-9]*,[0-9]+\.{0,1}[0-9]*)$/", trimToSingleLine($MDUIGeolocationHintEntry->nodeValue), $splitGeo)){
			$Entity[] = trimToSingleLine($splitGeo[1]);
haemmer's avatar
haemmer committed
688 689 690 691 692 693
		}
	}
	
	return $Entity;
}

694 695 696 697 698 699 700 701 702 703 704
/******************************************************************************/
// Get Organization Names from RoleDescriptor
function getOrganizationNames($RoleDescriptorNode){
	
	$Entity = Array();
	
	$Orgnization = $RoleDescriptorNode->parentNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:metadata', 'Organization' )->item(0);
	if ($Orgnization){
		$DisplayNames = $Orgnization->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:metadata', 'OrganizationDisplayName');
		foreach ($DisplayNames as $DisplayName){
			$lang = $DisplayName->getAttributeNodeNS('http://www.w3.org/XML/1998/namespace', 'lang')->nodeValue;
haemmer's avatar
haemmer committed
705
			$Entity[$lang] = trimToSingleLine($DisplayName->nodeValue);
706 707 708 709 710 711 712
		}
	}
	
	return $Entity;
}

/******************************************************************************/
haemmer's avatar
haemmer committed
713
// Get Attribute Consuming Service
714 715 716 717
function getAttributeConsumingServiceNames($RoleDescriptorNode){
	
	$Entity = Array();
	
718 719 720
	$ServiceNames = $RoleDescriptorNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:metadata', 'ServiceName' );
	foreach ($ServiceNames as $ServiceName){
		$lang = $ServiceName->getAttributeNodeNS('http://www.w3.org/XML/1998/namespace', 'lang')->nodeValue;
haemmer's avatar
haemmer committed
721
		$Entity[$lang] = trimToSingleLine($ServiceName->nodeValue);
722 723 724 725 726
	}
	
	return $Entity;
}

727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
/******************************************************************************/
// Returns true if IdP has Hide-From-Discovery entity category attribute
function hasHideFromDiscoveryEntityCategory($IDPRoleDescriptorNode){
	// Get SAML Attributes for this entity
	$AttributeValues = $IDPRoleDescriptorNode->parentNode->getElementsByTagNameNS('urn:oasis:names:tc:SAML:2.0:assertion', 'AttributeValue');
	
	if (!$AttributeValues || $AttributeValues->length < 1){
		return false;
	}
	
	foreach( $AttributeValues as $AttributeValue ){
		if (trim($AttributeValue->nodeValue) == 'http://refeds.org/category/hide-from-discovery'){
			return true;
		}
	}
	
	return false;
}

haemmer's avatar
haemmer committed
746
?>