1<?php 2 3/** 4 * Configuration of the OneLogin PHP Toolkit 5 * 6 */ 7 8class OneLogin_Saml2_Settings 9{ 10 /** 11 * List of paths. 12 * 13 * @var array 14 */ 15 private $_paths = array(); 16 17 /** 18 * @var string 19 */ 20 private $_baseurl; 21 22 /** 23 * Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages 24 * if it expects them signed or encrypted. If not, the messages will be accepted 25 * and some security issues will be also relaxed. 26 * 27 * @var bool 28 */ 29 private $_strict = true; 30 31 /** 32 * Activate debug mode 33 * 34 * @var bool 35 */ 36 private $_debug = false; 37 38 /** 39 * SP data. 40 * 41 * @var array 42 */ 43 private $_sp = array(); 44 45 /** 46 * IdP data. 47 * 48 * @var array 49 */ 50 private $_idp = array(); 51 52 /** 53 * Compression settings that determine 54 * whether gzip compression should be used. 55 * 56 * @var array 57 */ 58 private $_compress = array(); 59 60 /** 61 * Security Info related to the SP. 62 * 63 * @var array 64 */ 65 private $_security = array(); 66 67 /** 68 * Setting contacts. 69 * 70 * @var array 71 */ 72 private $_contacts = array(); 73 74 /** 75 * Setting organization. 76 * 77 * @var array 78 */ 79 private $_organization = array(); 80 81 /** 82 * Setting errors. 83 * 84 * @var array 85 */ 86 private $_errors = array(); 87 88 /** 89 * Setting errors. 90 * 91 * @var bool 92 */ 93 private $_spValidationOnly = false; 94 95 /** 96 * Initializes the settings: 97 * - Sets the paths of the different folders 98 * - Loads settings info from settings file or array/object provided 99 * 100 * @param array|object|null $settings SAML Toolkit Settings 101 * @param bool $spValidationOnly 102 * 103 * @throws OneLogin_Saml2_Error If any settings parameter is invalid 104 * @throws Exception If OneLogin_Saml2_Settings is incorrectly supplied 105 */ 106 public function __construct($settings = null, $spValidationOnly = false) 107 { 108 $this->_spValidationOnly = $spValidationOnly; 109 $this->_loadPaths(); 110 111 if (!isset($settings)) { 112 if (!$this->_loadSettingsFromFile()) { 113 throw new OneLogin_Saml2_Error( 114 'Invalid file settings: %s', 115 OneLogin_Saml2_Error::SETTINGS_INVALID, 116 array(implode(', ', $this->_errors)) 117 ); 118 } 119 $this->_addDefaultValues(); 120 } else if (is_array($settings)) { 121 if (!$this->_loadSettingsFromArray($settings)) { 122 throw new OneLogin_Saml2_Error( 123 'Invalid array settings: %s', 124 OneLogin_Saml2_Error::SETTINGS_INVALID, 125 array(implode(', ', $this->_errors)) 126 ); 127 } 128 } else if ($settings instanceof OneLogin_Saml2_Settings) { 129 throw new OneLogin_Saml2_Error( 130 'Only instances of OneLogin_Saml_Settings are supported.', 131 OneLogin_Saml2_Error::UNSUPPORTED_SETTINGS_OBJECT, 132 array(implode(', ', $this->_errors)) 133 ); 134 } else { 135 if (!$this->_loadSettingsFromArray($settings->getValues())) { 136 throw new OneLogin_Saml2_Error( 137 'Invalid array settings: %s', 138 OneLogin_Saml2_Error::SETTINGS_INVALID, 139 array(implode(', ', $this->_errors)) 140 ); 141 } 142 } 143 144 $this->formatIdPCert(); 145 $this->formatSPCert(); 146 $this->formatSPKey(); 147 $this->formatSPCertNew(); 148 $this->formatIdPCertMulti(); 149 } 150 151 /** 152 * Sets the paths of the different folders 153 * @suppress PhanUndeclaredConstant 154 */ 155 private function _loadPaths() 156 { 157 $basePath = dirname(dirname(__DIR__)).'/'; 158 $this->_paths = array ( 159 'base' => $basePath, 160 'config' => $basePath, 161 'cert' => $basePath.'certs/', 162 'lib' => $basePath.'lib/Saml2/', 163 'extlib' => $basePath.'extlib/' 164 ); 165 166 if (defined('ONELOGIN_CUSTOMPATH')) { 167 $this->_paths['config'] = ONELOGIN_CUSTOMPATH; 168 $this->_paths['cert'] = ONELOGIN_CUSTOMPATH.'certs/'; 169 } 170 } 171 172 /** 173 * Returns base path. 174 * 175 * @return string The base toolkit folder path 176 */ 177 public function getBasePath() 178 { 179 return $this->_paths['base']; 180 } 181 182 /** 183 * Returns cert path. 184 * 185 * @return string The cert folder path 186 */ 187 public function getCertPath() 188 { 189 return $this->_paths['cert']; 190 } 191 192 /** 193 * Returns config path. 194 * 195 * @return string The config folder path 196 */ 197 public function getConfigPath() 198 { 199 return $this->_paths['config']; 200 } 201 202 /** 203 * Returns lib path. 204 * 205 * @return string The library folder path 206 */ 207 public function getLibPath() 208 { 209 return $this->_paths['lib']; 210 } 211 212 /** 213 * Returns external lib path. 214 * 215 * @return string The external library folder path 216 */ 217 public function getExtLibPath() 218 { 219 return $this->_paths['extlib']; 220 } 221 222 /** 223 * Returns schema path. 224 * 225 * @return string The external library folder path 226 */ 227 public function getSchemasPath() 228 { 229 if (isset($this->_paths['schemas'])) { 230 return $this->_paths['schemas']; 231 } 232 return __DIR__ . '/schemas/'; 233 } 234 235 /** 236 * Set schemas path 237 * 238 * @param string $path 239 * @return $this 240 */ 241 public function setSchemasPath($path) 242 { 243 $this->_paths['schemas'] = $path; 244 } 245 246 /** 247 * Loads settings info from a settings Array 248 * 249 * @param array $settings SAML Toolkit Settings 250 * 251 * @return bool True if the settings info is valid 252 */ 253 private function _loadSettingsFromArray($settings) 254 { 255 if (isset($settings['sp'])) { 256 $this->_sp = $settings['sp']; 257 } 258 if (isset($settings['idp'])) { 259 $this->_idp = $settings['idp']; 260 } 261 262 $errors = $this->checkSettings($settings); 263 if (empty($errors)) { 264 $this->_errors = array(); 265 266 if (isset($settings['strict'])) { 267 $this->_strict = $settings['strict']; 268 } 269 if (isset($settings['debug'])) { 270 $this->_debug = $settings['debug']; 271 } 272 273 if (isset($settings['baseurl'])) { 274 $this->_baseurl = $settings['baseurl']; 275 } 276 277 if (isset($settings['compress'])) { 278 $this->_compress = $settings['compress']; 279 } 280 281 if (isset($settings['security'])) { 282 $this->_security = $settings['security']; 283 } 284 285 if (isset($settings['contactPerson'])) { 286 $this->_contacts = $settings['contactPerson']; 287 } 288 289 if (isset($settings['organization'])) { 290 $this->_organization = $settings['organization']; 291 } 292 293 $this->_addDefaultValues(); 294 return true; 295 } else { 296 $this->_errors = $errors; 297 return false; 298 } 299 } 300 301 /** 302 * Loads settings info from the settings file 303 * 304 * @return bool True if the settings info is valid 305 * 306 * @throws OneLogin_Saml2_Error 307 * 308 * @suppress PhanUndeclaredVariable 309 */ 310 private function _loadSettingsFromFile() 311 { 312 $filename = $this->getConfigPath().'settings.php'; 313 314 if (!file_exists($filename)) { 315 throw new OneLogin_Saml2_Error( 316 'Settings file not found: %s', 317 OneLogin_Saml2_Error::SETTINGS_FILE_NOT_FOUND, 318 array($filename) 319 ); 320 } 321 322 /** @var array $settings */ 323 include $filename; 324 325 // Add advance_settings if exists 326 327 $advancedFilename = $this->getConfigPath().'advanced_settings.php'; 328 329 if (file_exists($advancedFilename)) { 330 /** @var array $advancedSettings */ 331 include $advancedFilename; 332 $settings = array_merge($settings, $advancedSettings); 333 } 334 335 336 return $this->_loadSettingsFromArray($settings); 337 } 338 339 /** 340 * Add default values if the settings info is not complete 341 */ 342 private function _addDefaultValues() 343 { 344 if (!isset($this->_sp['assertionConsumerService']['binding'])) { 345 $this->_sp['assertionConsumerService']['binding'] = OneLogin_Saml2_Constants::BINDING_HTTP_POST; 346 } 347 if (isset($this->_sp['singleLogoutService']) && !isset($this->_sp['singleLogoutService']['binding'])) { 348 $this->_sp['singleLogoutService']['binding'] = OneLogin_Saml2_Constants::BINDING_HTTP_REDIRECT; 349 } 350 351 if (!isset($this->_compress['requests'])) { 352 $this->_compress['requests'] = true; 353 } 354 355 if (!isset($this->_compress['responses'])) { 356 $this->_compress['responses'] = true; 357 } 358 359 // Related to nameID 360 if (!isset($this->_sp['NameIDFormat'])) { 361 $this->_sp['NameIDFormat'] = OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED; 362 } 363 if (!isset($this->_security['nameIdEncrypted'])) { 364 $this->_security['nameIdEncrypted'] = false; 365 } 366 if (!isset($this->_security['requestedAuthnContext'])) { 367 $this->_security['requestedAuthnContext'] = true; 368 } 369 370 // sign provided 371 if (!isset($this->_security['authnRequestsSigned'])) { 372 $this->_security['authnRequestsSigned'] = false; 373 } 374 if (!isset($this->_security['logoutRequestSigned'])) { 375 $this->_security['logoutRequestSigned'] = false; 376 } 377 if (!isset($this->_security['logoutResponseSigned'])) { 378 $this->_security['logoutResponseSigned'] = false; 379 } 380 if (!isset($this->_security['signMetadata'])) { 381 $this->_security['signMetadata'] = false; 382 } 383 384 // sign expected 385 if (!isset($this->_security['wantMessagesSigned'])) { 386 $this->_security['wantMessagesSigned'] = false; 387 } 388 if (!isset($this->_security['wantAssertionsSigned'])) { 389 $this->_security['wantAssertionsSigned'] = false; 390 } 391 392 // NameID element expected 393 if (!isset($this->_security['wantNameId'])) { 394 $this->_security['wantNameId'] = true; 395 } 396 397 // Relax Destination validation 398 if (!isset($this->_security['relaxDestinationValidation'])) { 399 $this->_security['relaxDestinationValidation'] = false; 400 } 401 402 403 // Strict Destination match validation 404 if (!isset($this->_security['destinationStrictlyMatches'])) { 405 $this->_security['destinationStrictlyMatches'] = false; 406 } 407 408 // InResponseTo 409 if (!isset($this->_security['rejectUnsolicitedResponsesWithInResponseTo'])) { 410 $this->_security['rejectUnsolicitedResponsesWithInResponseTo'] = false; 411 } 412 413 // encrypt expected 414 if (!isset($this->_security['wantAssertionsEncrypted'])) { 415 $this->_security['wantAssertionsEncrypted'] = false; 416 } 417 if (!isset($this->_security['wantNameIdEncrypted'])) { 418 $this->_security['wantNameIdEncrypted'] = false; 419 } 420 421 // XML validation 422 if (!isset($this->_security['wantXMLValidation'])) { 423 $this->_security['wantXMLValidation'] = true; 424 } 425 426 // SignatureAlgorithm 427 if (!isset($this->_security['signatureAlgorithm'])) { 428 $this->_security['signatureAlgorithm'] = XMLSecurityKey::RSA_SHA1; 429 } 430 431 // DigestAlgorithm 432 if (!isset($this->_security['digestAlgorithm'])) { 433 $this->_security['digestAlgorithm'] = XMLSecurityDSig::SHA1; 434 } 435 436 if (!isset($this->_security['lowercaseUrlencoding'])) { 437 $this->_security['lowercaseUrlencoding'] = false; 438 } 439 440 // Certificates / Private key /Fingerprint 441 if (!isset($this->_idp['x509cert'])) { 442 $this->_idp['x509cert'] = ''; 443 } 444 if (!isset($this->_idp['certFingerprint'])) { 445 $this->_idp['certFingerprint'] = ''; 446 } 447 if (!isset($this->_idp['certFingerprintAlgorithm'])) { 448 $this->_idp['certFingerprintAlgorithm'] = 'sha1'; 449 } 450 451 if (!isset($this->_sp['x509cert'])) { 452 $this->_sp['x509cert'] = ''; 453 } 454 if (!isset($this->_sp['privateKey'])) { 455 $this->_sp['privateKey'] = ''; 456 } 457 } 458 459 /** 460 * Checks the settings info. 461 * 462 * @param array $settings Array with settings data 463 * 464 * @return array $errors Errors found on the settings data 465 */ 466 public function checkSettings($settings) 467 { 468 assert('is_array($settings)'); 469 470 if (!is_array($settings) || empty($settings)) { 471 $errors = array('invalid_syntax'); 472 } else { 473 $errors = array(); 474 if (!$this->_spValidationOnly) { 475 $idpErrors = $this->checkIdPSettings($settings); 476 $errors = array_merge($idpErrors, $errors); 477 } 478 $spErrors = $this->checkSPSettings($settings); 479 $errors = array_merge($spErrors, $errors); 480 481 $compressErrors = $this->checkCompressionSettings($settings); 482 $errors = array_merge($compressErrors, $errors); 483 } 484 485 return $errors; 486 } 487 488 /** 489 * Checks the compression settings info. 490 * 491 * @param array $settings Array with settings data 492 * 493 * @return array $errors Errors found on the settings data 494 */ 495 public function checkCompressionSettings($settings) 496 { 497 $errors = array(); 498 499 if (isset($settings['compress'])) { 500 if (!is_array($settings['compress'])) { 501 $errors[] = "invalid_syntax"; 502 } else if (isset($settings['compress']['requests']) 503 && $settings['compress']['requests'] !== true 504 && $settings['compress']['requests'] !== false 505 ) { 506 $errors[] = "'compress'=>'requests' values must be true or false."; 507 } else if (isset($settings['compress']['responses']) 508 && $settings['compress']['responses'] !== true 509 && $settings['compress']['responses'] !== false 510 ) { 511 $errors[] = "'compress'=>'responses' values must be true or false."; 512 } 513 } 514 return $errors; 515 } 516 517 /** 518 * Checks the IdP settings info. 519 * 520 * @param array $settings Array with settings data 521 * 522 * @return array $errors Errors found on the IdP settings data 523 */ 524 public function checkIdPSettings($settings) 525 { 526 assert('is_array($settings)'); 527 528 if (!is_array($settings) || empty($settings)) { 529 return array('invalid_syntax'); 530 } 531 532 $errors = array(); 533 534 if (!isset($settings['idp']) || empty($settings['idp'])) { 535 $errors[] = 'idp_not_found'; 536 } else { 537 $idp = $settings['idp']; 538 if (!isset($idp['entityId']) || empty($idp['entityId'])) { 539 $errors[] = 'idp_entityId_not_found'; 540 } 541 542 if (!isset($idp['singleSignOnService']) 543 || !isset($idp['singleSignOnService']['url']) 544 || empty($idp['singleSignOnService']['url']) 545 ) { 546 $errors[] = 'idp_sso_not_found'; 547 } else if (!filter_var($idp['singleSignOnService']['url'], FILTER_VALIDATE_URL)) { 548 $errors[] = 'idp_sso_url_invalid'; 549 } 550 551 if (isset($idp['singleLogoutService']) 552 && isset($idp['singleLogoutService']['url']) 553 && !empty($idp['singleLogoutService']['url']) 554 && !filter_var($idp['singleLogoutService']['url'], FILTER_VALIDATE_URL) 555 ) { 556 $errors[] = 'idp_slo_url_invalid'; 557 } 558 559 if (isset($idp['singleLogoutService']) 560 && isset($idp['singleLogoutService']['responseUrl']) 561 && !empty($idp['singleLogoutService']['responseUrl']) 562 && !filter_var($idp['singleLogoutService']['responseUrl'], FILTER_VALIDATE_URL) 563 ) { 564 $errors[] = 'idp_slo_response_url_invalid'; 565 } 566 567 if (isset($settings['security'])) { 568 $security = $settings['security']; 569 570 $existsX509 = isset($idp['x509cert']) && !empty($idp['x509cert']); 571 $existsMultiX509Sign = isset($idp['x509certMulti']) && isset($idp['x509certMulti']['signing']) && !empty($idp['x509certMulti']['signing']); 572 $existsMultiX509Enc = isset($idp['x509certMulti']) && isset($idp['x509certMulti']['encryption']) && !empty($idp['x509certMulti']['encryption']); 573 574 $existsFingerprint = isset($idp['certFingerprint']) && !empty($idp['certFingerprint']); 575 if (!($existsX509 || $existsFingerprint || $existsMultiX509Sign) 576 ) { 577 $errors[] = 'idp_cert_or_fingerprint_not_found_and_required'; 578 } 579 if ((isset($security['nameIdEncrypted']) && $security['nameIdEncrypted'] == true) 580 && !($existsX509 || $existsMultiX509Enc) 581 ) { 582 $errors[] = 'idp_cert_not_found_and_required'; 583 } 584 } 585 } 586 587 return $errors; 588 } 589 590 /** 591 * Checks the SP settings info. 592 * 593 * @param array $settings Array with settings data 594 * 595 * @return array $errors Errors found on the SP settings data 596 */ 597 public function checkSPSettings($settings) 598 { 599 assert('is_array($settings)'); 600 601 if (!is_array($settings) || empty($settings)) { 602 return array('invalid_syntax'); 603 } 604 605 $errors = array(); 606 607 if (!isset($settings['sp']) || empty($settings['sp'])) { 608 $errors[] = 'sp_not_found'; 609 } else { 610 $sp = $settings['sp']; 611 $security = array(); 612 if (isset($settings['security'])) { 613 $security = $settings['security']; 614 } 615 616 if (!isset($sp['entityId']) || empty($sp['entityId'])) { 617 $errors[] = 'sp_entityId_not_found'; 618 } 619 620 if (!isset($sp['assertionConsumerService']) 621 || !isset($sp['assertionConsumerService']['url']) 622 || empty($sp['assertionConsumerService']['url']) 623 ) { 624 $errors[] = 'sp_acs_not_found'; 625 } else if (!filter_var($sp['assertionConsumerService']['url'], FILTER_VALIDATE_URL)) { 626 $errors[] = 'sp_acs_url_invalid'; 627 } 628 629 if (isset($sp['singleLogoutService']) 630 && isset($sp['singleLogoutService']['url']) 631 && !filter_var($sp['singleLogoutService']['url'], FILTER_VALIDATE_URL) 632 ) { 633 $errors[] = 'sp_sls_url_invalid'; 634 } 635 636 if (isset($security['signMetadata']) && is_array($security['signMetadata'])) { 637 if ((!isset($security['signMetadata']['keyFileName']) 638 || !isset($security['signMetadata']['certFileName'])) && 639 (!isset($security['signMetadata']['privateKey']) 640 || !isset($security['signMetadata']['x509cert'])) 641 ) { 642 $errors[] = 'sp_signMetadata_invalid'; 643 } 644 } 645 646 if (((isset($security['authnRequestsSigned']) && $security['authnRequestsSigned'] == true) 647 || (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned'] == true) 648 || (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned'] == true) 649 || (isset($security['wantAssertionsEncrypted']) && $security['wantAssertionsEncrypted'] == true) 650 || (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted'] == true)) 651 && !$this->checkSPCerts() 652 ) { 653 $errors[] = 'sp_certs_not_found_and_required'; 654 } 655 } 656 657 if (isset($settings['contactPerson'])) { 658 $types = array_keys($settings['contactPerson']); 659 $validTypes = array('technical', 'support', 'administrative', 'billing', 'other'); 660 foreach ($types as $type) { 661 if (!in_array($type, $validTypes)) { 662 $errors[] = 'contact_type_invalid'; 663 break; 664 } 665 } 666 667 foreach ($settings['contactPerson'] as $type => $contact) { 668 if (!isset($contact['givenName']) || empty($contact['givenName']) 669 || !isset($contact['emailAddress']) || empty($contact['emailAddress']) 670 ) { 671 $errors[] = 'contact_not_enought_data'; 672 break; 673 } 674 } 675 } 676 677 if (isset($settings['organization'])) { 678 foreach ($settings['organization'] as $organization) { 679 if (!isset($organization['name']) || empty($organization['name']) 680 || !isset($organization['displayname']) || empty($organization['displayname']) 681 || !isset($organization['url']) || empty($organization['url']) 682 ) { 683 $errors[] = 'organization_not_enought_data'; 684 break; 685 } 686 } 687 } 688 689 return $errors; 690 } 691 692 /** 693 * Checks if the x509 certs of the SP exists and are valid. 694 * 695 * @return bool 696 */ 697 public function checkSPCerts() 698 { 699 $key = $this->getSPkey(); 700 $cert = $this->getSPcert(); 701 return (!empty($key) && !empty($cert)); 702 } 703 704 /** 705 * Returns the x509 private key of the SP. 706 * 707 * @return string SP private key 708 */ 709 public function getSPkey() 710 { 711 $key = null; 712 if (isset($this->_sp['privateKey']) && !empty($this->_sp['privateKey'])) { 713 $key = $this->_sp['privateKey']; 714 } else { 715 $keyFile = $this->_paths['cert'].'sp.key'; 716 717 if (file_exists($keyFile)) { 718 $key = file_get_contents($keyFile); 719 } 720 } 721 return $key; 722 } 723 724 /** 725 * Returns the x509 public cert of the SP. 726 * 727 * @return string SP public cert 728 */ 729 public function getSPcert() 730 { 731 $cert = null; 732 733 if (isset($this->_sp['x509cert']) && !empty($this->_sp['x509cert'])) { 734 $cert = $this->_sp['x509cert']; 735 } else { 736 $certFile = $this->_paths['cert'].'sp.crt'; 737 738 if (file_exists($certFile)) { 739 $cert = file_get_contents($certFile); 740 } 741 } 742 return $cert; 743 } 744 745 /** 746 * Returns the x509 public of the SP that is 747 * planed to be used soon instead the other 748 * public cert 749 * @return string SP public cert New 750 */ 751 public function getSPcertNew() 752 { 753 $cert = null; 754 755 if (isset($this->_sp['x509certNew']) && !empty($this->_sp['x509certNew'])) { 756 $cert = $this->_sp['x509certNew']; 757 } else { 758 $certFile = $this->_paths['cert'].'sp_new.crt'; 759 760 if (file_exists($certFile)) { 761 $cert = file_get_contents($certFile); 762 } 763 } 764 return $cert; 765 } 766 767 /** 768 * Gets the IdP data. 769 * 770 * @return array IdP info 771 */ 772 public function getIdPData() 773 { 774 return $this->_idp; 775 } 776 777 /** 778 * Gets the SP data. 779 * 780 * @return array SP info 781 */ 782 public function getSPData() 783 { 784 return $this->_sp; 785 } 786 787 /** 788 * Gets security data. 789 * 790 * @return array SP info 791 */ 792 public function getSecurityData() 793 { 794 return $this->_security; 795 } 796 797 /** 798 * Gets contact data. 799 * 800 * @return array SP info 801 */ 802 public function getContacts() 803 { 804 return $this->_contacts; 805 } 806 807 /** 808 * Gets organization data. 809 * 810 * @return array SP info 811 */ 812 public function getOrganization() 813 { 814 return $this->_organization; 815 } 816 817 /** 818 * Should SAML requests be compressed? 819 * 820 * @return bool Yes/No as True/False 821 */ 822 public function shouldCompressRequests() 823 { 824 return $this->_compress['requests']; 825 } 826 827 /** 828 * Should SAML responses be compressed? 829 * 830 * @return bool Yes/No as True/False 831 */ 832 public function shouldCompressResponses() 833 { 834 return $this->_compress['responses']; 835 } 836 837 /** 838 * Gets the SP metadata. The XML representation. 839 * 840 * @param bool $alwaysPublishEncryptionCert When 'true', the returned metadata 841 * will always include an 'encryption' KeyDescriptor. Otherwise, the 'encryption' 842 * KeyDescriptor will only be included if $advancedSettings['security']['wantNameIdEncrypted'] 843 * or $advancedSettings['security']['wantAssertionsEncrypted'] are enabled. 844 * @param DateTime|null $validUntil Metadata's valid time 845 * @param int|null $cacheDuration Duration of the cache in seconds 846 * 847 * @return string SP metadata (xml) 848 * 849 * @throws Exception 850 * @throws OneLogin_Saml2_Error 851 */ 852 public function getSPMetadata($alwaysPublishEncryptionCert = false, $validUntil = null, $cacheDuration = null) 853 { 854 $metadata = OneLogin_Saml2_Metadata::builder($this->_sp, $this->_security['authnRequestsSigned'], $this->_security['wantAssertionsSigned'], $validUntil, $cacheDuration, $this->getContacts(), $this->getOrganization()); 855 856 $certNew = $this->getSPcertNew(); 857 if (!empty($certNew)) { 858 $metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors( 859 $metadata, 860 $certNew, 861 $alwaysPublishEncryptionCert || $this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted'] 862 ); 863 } 864 865 $cert = $this->getSPcert(); 866 if (!empty($cert)) { 867 $metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors( 868 $metadata, 869 $cert, 870 $alwaysPublishEncryptionCert || $this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted'] 871 ); 872 } 873 874 //Sign Metadata 875 if (isset($this->_security['signMetadata']) && $this->_security['signMetadata'] !== false) { 876 if ($this->_security['signMetadata'] === true) { 877 $keyMetadata = $this->getSPkey(); 878 $certMetadata = $cert; 879 if (!$keyMetadata) { 880 throw new OneLogin_Saml2_Error( 881 'SP Private key not found.', 882 OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND 883 ); 884 } 885 if (!$certMetadata) { 886 throw new OneLogin_Saml2_Error( 887 'SP Public cert not found.', 888 OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND 889 ); 890 } 891 } else if (isset($this->_security['signMetadata']['keyFileName']) && 892 isset($this->_security['signMetadata']['certFileName'])) { 893 $keyFileName = $this->_security['signMetadata']['keyFileName']; 894 $certFileName = $this->_security['signMetadata']['certFileName']; 895 $keyMetadataFile = $this->_paths['cert'].$keyFileName; 896 $certMetadataFile = $this->_paths['cert'].$certFileName; 897 if (!file_exists($keyMetadataFile)) { 898 throw new OneLogin_Saml2_Error( 899 'SP Private key file not found: %s', 900 OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND, 901 array($keyMetadataFile) 902 ); 903 } 904 if (!file_exists($certMetadataFile)) { 905 throw new OneLogin_Saml2_Error( 906 'SP Public cert file not found: %s', 907 OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND, 908 array($certMetadataFile) 909 ); 910 } 911 $keyMetadata = file_get_contents($keyMetadataFile); 912 $certMetadata = file_get_contents($certMetadataFile); 913 } else if (isset($this->_security['signMetadata']['privateKey']) && 914 isset($this->_security['signMetadata']['x509cert'])) { 915 $keyMetadata = OneLogin_Saml2_Utils::formatPrivateKey($this->_security['signMetadata']['privateKey']); 916 $certMetadata = OneLogin_Saml2_Utils::formatCert($this->_security['signMetadata']['x509cert']); 917 if (!$keyMetadata) { 918 throw new OneLogin_Saml2_Error( 919 'Private key not found.', 920 OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND 921 ); 922 } 923 if (!$certMetadata) { 924 throw new OneLogin_Saml2_Error( 925 'Public cert not found.', 926 OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND 927 ); 928 } 929 } else { 930 throw new OneLogin_Saml2_Error( 931 'Invalid Setting: signMetadata value of the sp is not valid', 932 OneLogin_Saml2_Error::SETTINGS_INVALID_SYNTAX 933 ); 934 } 935 936 $signatureAlgorithm = $this->_security['signatureAlgorithm']; 937 $digestAlgorithm = $this->_security['digestAlgorithm']; 938 $metadata = OneLogin_Saml2_Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm, $digestAlgorithm); 939 } 940 return $metadata; 941 } 942 943 /** 944 * Validates an XML SP Metadata. 945 * 946 * @param string $xml Metadata's XML that will be validate 947 * 948 * @return Array The list of found errors 949 * 950 * @throws Exception 951 */ 952 public function validateMetadata($xml) 953 { 954 assert('is_string($xml)'); 955 956 $errors = array(); 957 $res = OneLogin_Saml2_Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug, $this->getSchemasPath()); 958 if (!$res instanceof DOMDocument) { 959 $errors[] = $res; 960 } else { 961 $dom = $res; 962 $element = $dom->documentElement; 963 if ($element->tagName !== 'md:EntityDescriptor') { 964 $errors[] = 'noEntityDescriptor_xml'; 965 } else { 966 $validUntil = $cacheDuration = $expireTime = null; 967 968 if ($element->hasAttribute('validUntil')) { 969 $validUntil = OneLogin_Saml2_Utils::parseSAML2Time($element->getAttribute('validUntil')); 970 } 971 if ($element->hasAttribute('cacheDuration')) { 972 $cacheDuration = $element->getAttribute('cacheDuration'); 973 } 974 975 $expireTime = OneLogin_Saml2_Utils::getExpireTime($cacheDuration, $validUntil); 976 if (isset($expireTime) && time() > $expireTime) { 977 $errors[] = 'expired_xml'; 978 } 979 } 980 } 981 982 // TODO: Support Metadata Sign Validation 983 984 return $errors; 985 } 986 987 /** 988 * Formats the IdP cert. 989 */ 990 public function formatIdPCert() 991 { 992 if (isset($this->_idp['x509cert'])) { 993 $this->_idp['x509cert'] = OneLogin_Saml2_Utils::formatCert($this->_idp['x509cert']); 994 } 995 } 996 997 /** 998 * Formats the Multple IdP certs. 999 */ 1000 public function formatIdPCertMulti() 1001 { 1002 if (isset($this->_idp['x509certMulti'])) { 1003 if (isset($this->_idp['x509certMulti']['signing'])) { 1004 foreach ($this->_idp['x509certMulti']['signing'] as $i => $cert) { 1005 $this->_idp['x509certMulti']['signing'][$i] = OneLogin_Saml2_Utils::formatCert($cert); 1006 } 1007 } 1008 if (isset($this->_idp['x509certMulti']['encryption'])) { 1009 foreach ($this->_idp['x509certMulti']['encryption'] as $i => $cert) { 1010 $this->_idp['x509certMulti']['encryption'][$i] = OneLogin_Saml2_Utils::formatCert($cert); 1011 } 1012 } 1013 } 1014 } 1015 1016 /** 1017 * Formats the SP cert. 1018 */ 1019 public function formatSPCert() 1020 { 1021 if (isset($this->_sp['x509cert'])) { 1022 $this->_sp['x509cert'] = OneLogin_Saml2_Utils::formatCert($this->_sp['x509cert']); 1023 } 1024 } 1025 1026 /** 1027 * Formats the SP cert. 1028 */ 1029 public function formatSPCertNew() 1030 { 1031 if (isset($this->_sp['x509certNew'])) { 1032 $this->_sp['x509certNew'] = OneLogin_Saml2_Utils::formatCert($this->_sp['x509certNew']); 1033 } 1034 } 1035 1036 /** 1037 * Formats the SP private key. 1038 */ 1039 public function formatSPKey() 1040 { 1041 if (isset($this->_sp['privateKey'])) { 1042 $this->_sp['privateKey'] = OneLogin_Saml2_Utils::formatPrivateKey($this->_sp['privateKey']); 1043 } 1044 } 1045 1046 /** 1047 * Returns an array with the errors, the array is empty when the settings is ok. 1048 * 1049 * @return array Errors 1050 */ 1051 public function getErrors() 1052 { 1053 return $this->_errors; 1054 } 1055 1056 /** 1057 * Activates or deactivates the strict mode. 1058 * 1059 * @param bool $value Strict parameter 1060 * 1061 * @throws Exception 1062 */ 1063 public function setStrict($value) 1064 { 1065 if (!is_bool($value)) { 1066 throw new Exception('Invalid value passed to setStrict()'); 1067 } 1068 1069 $this->_strict = $value; 1070 } 1071 1072 /** 1073 * Returns if the 'strict' mode is active. 1074 * 1075 * @return bool Strict parameter 1076 */ 1077 public function isStrict() 1078 { 1079 return $this->_strict; 1080 } 1081 1082 /** 1083 * Returns if the debug is active. 1084 * 1085 * @return bool Debug parameter 1086 */ 1087 public function isDebugActive() 1088 { 1089 return $this->_debug; 1090 } 1091 1092 /** 1093 * Set a baseurl value. 1094 * 1095 * @param $baseurl 1096 */ 1097 public function setBaseURL($baseurl) 1098 { 1099 $this->_baseurl = $baseurl; 1100 } 1101 1102 /** 1103 * Returns the baseurl set on the settings if any. 1104 * 1105 * @return null|string The baseurl 1106 */ 1107 public function getBaseURL() 1108 { 1109 return $this->_baseurl; 1110 } 1111 1112 /** 1113 * Sets the IdP certificate. 1114 * 1115 * @param string $cert IdP certificate 1116 */ 1117 public function setIdPCert($cert) 1118 { 1119 $this->_idp['x509cert'] = $cert; 1120 $this->formatIdPCert(); 1121 } 1122} 1123