Skip to content
Snippets Groups Projects
SAMLParserTest.php 8.83 KiB
Newer Older
Tim van Dijen's avatar
Tim van Dijen committed
namespace SimpleSAML\Test\Metadata;
require_once(__DIR__.'/../../../SigningTestCase.php');

use RobRichards\XMLSecLibs\XMLSecurityDSig;
use \SimpleSAML\XML\Signer;
use \SimpleSAML\Metadata\SAMLParser;
/**
 * Test SAML parsing
 */
class SAMLParserTest extends \SimpleSAML\Test\SigningTestCase
{
    /**
     * Test Registration Info is parsed
     */
    public function testRegistrationInfo()
    {
        $expected = [
            'registrationAuthority' => 'https://incommon.org',
        $document = \SAML2\DOMDocumentFactory::fromString(
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi">
  <EntityDescriptor entityID="theEntityID">
    <Extensions>
      <mdrpi:RegistrationInfo registrationAuthority="https://incommon.org"/>
    </Extensions>
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
  </EntityDescriptor>
</EntitiesDescriptor>
XML
        );


        $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
        $this->assertArrayHasKey('theEntityID', $entities);
        // RegistrationInfo is accessible in the SP or IDP metadata accessors
        $metadata = $entities['theEntityID']->getMetadata20SP();
        $this->assertEquals($expected, $metadata['RegistrationInfo']);
    }

    /**
     * Test RegistrationInfo is inherited correctly from parent EntitiesDescriptor.
     * According to the spec overriding RegistrationInfo is not valid. We ignore attempts to override
     */
    public function testRegistrationInfoInheritance()
    {
        $expected = [
            'registrationAuthority' => 'https://incommon.org',
        $document = \SAML2\DOMDocumentFactory::fromString(
            <<<XML
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi">
  <Extensions>
    <mdrpi:RegistrationInfo registrationAuthority="https://incommon.org"/>
  </Extensions>
  <EntityDescriptor entityID="theEntityID">
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
  </EntityDescriptor>
  <EntitiesDescriptor>
    <EntityDescriptor entityID="subEntityId">
      <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
    </EntityDescriptor>
    <EntityDescriptor entityID="subEntityIdOverride">
      <Extensions>
        <mdrpi:RegistrationInfo registrationAuthority="overrides-are-ignored"/>
      </Extensions>
      <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
    </EntityDescriptor>
  </EntitiesDescriptor>
</EntitiesDescriptor>
        $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
        $this->assertArrayHasKey('theEntityID', $entities);
        $this->assertArrayHasKey('subEntityId', $entities);
        // RegistrationInfo is accessible in the SP or IDP metadata accessors
        $metadata = $entities['theEntityID']->getMetadata20SP();
        $this->assertEquals($expected, $metadata['RegistrationInfo']);
        $metadata = $entities['subEntityId']->getMetadata20SP();
        $this->assertEquals($expected, $metadata['RegistrationInfo']);

        $metadata = $entities['subEntityIdOverride']->getMetadata20SP();
        $this->assertEquals($expected, $metadata['RegistrationInfo']);

    /**
     * Test AttributeConsumingService is parsed
     */
    public function testAttributeConsumingServiceParsing()
    {
        $document = \SAML2\DOMDocumentFactory::fromString(
            <<<XML
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi">
  <EntityDescriptor entityID="theEntityID">
    <Extensions>
      <mdrpi:RegistrationInfo registrationAuthority="https://incommon.org"/>
    </Extensions>
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
      <AttributeConsumingService index="0">
        <ServiceName xml:lang="en">Example service</ServiceName>
        <ServiceDescription xml:lang="nl">Dit is een voorbeeld voor de unittest.</ServiceDescription>

        <RequestedAttribute FriendlyName="eduPersonPrincipalName" Name="urn:mace:dir:attribute-def:eduPersonPrincipalName" NameFormat="urn:mace:shibboleth:1.0:attributeNamespace:uri" isRequired="true"/>
        <RequestedAttribute FriendlyName="mail" Name="urn:mace:dir:attribute-def:mail" NameFormat="urn:mace:shibboleth:1.0:attributeNamespace:uri"/>
        <RequestedAttribute FriendlyName="displayName" Name="urn:mace:dir:attribute-def:displayName" NameFormat="urn:mace:shibboleth:1.0:attributeNamespace:uri"/>
      </AttributeConsumingService>
    </SPSSODescriptor>

  </EntityDescriptor>
</EntitiesDescriptor>
XML
        );

        $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
        $this->assertArrayHasKey('theEntityID', $entities);

        $metadata = $entities['theEntityID']->getMetadata20SP();

        $this->assertEquals("Example service", $metadata['name']['en']);
        $this->assertEquals("Dit is een voorbeeld voor de unittest.", $metadata['description']['nl']);

Tim van Dijen's avatar
Tim van Dijen committed
        $expected_a = [
            "urn:mace:dir:attribute-def:eduPersonPrincipalName",
            "urn:mace:dir:attribute-def:mail",
            "urn:mace:dir:attribute-def:displayName"
        ];
        $expected_r = ["urn:mace:dir:attribute-def:eduPersonPrincipalName"];

        $this->assertEquals($expected_a, $metadata['attributes']);
        $this->assertEquals($expected_r, $metadata['attributes.required']);
    }


    public function makeTestDocument()
    {
        $doc = new \DOMDocument();
        $doc->loadXML(
            <<<XML
<?xml version="1.0"?>
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
  <EntityDescriptor entityID="theEntityID">
    <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
  </EntityDescriptor>
</EntitiesDescriptor>
XML
        );

        $entities_root = $doc->getElementsByTagName('EntitiesDescriptor')->item(0);
        $signer = new Signer([]);
        $signer->loadPrivateKey($this->good_private_key_file, null, true);
        $signer->loadCertificate($this->good_certificate_file, true);
        $signer->sign($entities_root, $entities_root);

        return $doc;
    }

    public function _testValidateFingerprint($algo, $expected_fingerprint)
    {
        $doc = $this->makeTestDocument();
        $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($doc->documentElement);
        foreach ($entities as $entity) {
            $this->assertTrue(
                $entity->validateFingerprint($expected_fingerprint, $algo)
            );
        }
    }


    public function testValidateFingerprintSHA1()
    {
        $this->_testValidateFingerprint(
            XMLSecurityDSig::SHA1,
            'A7:FB:75:22:57:88:A1:B0:D0:29:0A:4B:D1:EA:0C:01:F8:98:44:A0'
        );
    }


    public function testValidateFingerprintSHA256()
    {
        $this->_testValidateFingerprint(
            XMLSecurityDSig::SHA256,
            '3E:04:6B:2C:13:B5:02:FB:FC:93:66:EE:6C:A3:D1:BB:B8:9E:D8:38:03' .
            ':96:C5:C0:EC:95:D5:C9:F6:C1:D5:FC'
        );
    }


    public function testValidateFingerprintSHA384()
    {
        $this->_testValidateFingerprint(
            XMLSecurityDSig::SHA384,
            '38:87:CC:59:54:CF:ED:FC:71:B6:21:F3:8A:52:76:EF:30:C8:8C:A0:38' .
            ':48:77:87:58:14:A0:B3:55:EF:48:9C:B4:B3:44:1F:B7:BB:FC:28:65' .
            ':6E:93:83:52:C2:8E:A6'
        );
    }


    public function testValidateFingerprintSHA512()
    {
        $this->_testValidateFingerprint(
            XMLSecurityDSig::SHA512,
            '72:6C:51:01:A1:E9:76:D8:61:C4:B2:4F:AC:0B:64:7D:0D:4E:B7:DC:B3' .
            ':4A:92:23:51:A6:DC:A5:A1:9A:A5:DD:43:F5:05:6A:B7:7D:83:1F:B6:' .
            'CC:68:54:54:54:37:1B:EC:E1:22:5A:48:C6:BC:67:4B:A6:78:EE:E0:C6:8C:59'
        );
    }


    public function testValidateFingerprintUnknownAlgorithmThrows()
    {
        $doc = $this->makeTestDocument();
        $entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($doc->documentElement);
        foreach ($entities as $entity) {
            try {
                $entity->validateFingerprint('unused', 'invalid_algorithm');
            } catch (\UnexpectedValueException $e) {
                $this->assertEquals(
                    'Unsupported hashing function invalid_algorithm. Known options: [' .
                    'http://www.w3.org/2000/09/xmldsig#sha1, ' .
                    'http://www.w3.org/2001/04/xmlenc#sha256, ' .
                    'http://www.w3.org/2001/04/xmldsig-more#sha384, ' .
                    'http://www.w3.org/2001/04/xmlenc#sha512]',
                    $e->getMessage()
                );
            }
        }
    }