Newer
Older
use PHPUnit\Framework\TestCase;
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()
{
'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()
{
'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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
*/
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']);
$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;
}
* @param string $algo
* @param string $expected_fingerprint
private function validateFingerprint($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()
{
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()
{
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()
{
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()
{
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()
);
}
}
}
/**
* Test RoleDescriptor/Extensions is parsed
*/
public function testRoleDescriptorExtensions()
{
$expected = [
'scope' => [
'example.org',
'example.net',
],
'UIInfo' => [
'DisplayName' => ['en' => 'DisplayName', 'af' => 'VertoonNaam'],
'Description' => ['en' => 'Description',],
'InformationURL' => ['en' => 'https://localhost/information',],
'PrivacyStatementURL' => ['en' => 'https://localhost/privacypolicy',],
'Logo' => [
[
'url' => 'https://localhost/logo',
'height' => 16,
'width' => 17,
],
[
'url' => 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
'height' => 2,
'width' => 1,
],
],
],
'DiscoHints' => [
'IPHint' => ['127.0.0.1', '127.0.0.2',],
'DomainHint' => ['example.net', 'example.org',],
'GeolocationHint' => ['geo:-29.00000,24.00000;u=830000',],
],
'name' => ['en' => 'DisplayName', 'af' => 'VertoonNaam'],
];
$document = \SAML2\DOMDocumentFactory::fromString(
<<<XML
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
<EntityDescriptor entityID="theEntityID">
<IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<Extensions>
<shibmd:Scope regexp="false">example.org</shibmd:Scope>
<shibmd:Scope regexp="false">example.net</shibmd:Scope>
<mdui:UIInfo>
<mdui:DisplayName xml:lang="en">DisplayName</mdui:DisplayName>
<mdui:DisplayName xml:lang="af">VertoonNaam</mdui:DisplayName>
<mdui:Description xml:lang="en">Description</mdui:Description>
<mdui:PrivacyStatementURL xml:lang="en">https://localhost/privacypolicy</mdui:PrivacyStatementURL>
<mdui:InformationURL xml:lang="en">https://localhost/information</mdui:InformationURL>
<mdui:Logo width="17" height="16">https://localhost/logo</mdui:Logo>
<mdui:Logo width="1" height="2">data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==</mdui:Logo>
</mdui:UIInfo>
<mdui:DiscoHints>
<mdui:IPHint>127.0.0.1</mdui:IPHint>
<mdui:IPHint>127.0.0.2</mdui:IPHint>
<mdui:DomainHint>example.net</mdui:DomainHint>
<mdui:DomainHint>example.org</mdui:DomainHint>
<mdui:GeolocationHint>geo:-29.00000,24.00000;u=830000</mdui:GeolocationHint>
</mdui:DiscoHints>
</Extensions>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://IdentityProvider.com/SAML/SSO/Browser"/>
</IDPSSODescriptor>
</EntityDescriptor>
</EntitiesDescriptor>
XML
);
$entities = \SimpleSAML\Metadata\SAMLParser::parseDescriptorsElement($document->documentElement);
$this->assertArrayHasKey('theEntityID', $entities);
// Various MDUI elements are accessible
$metadata = $entities['theEntityID']->getMetadata20IdP();
$this->assertEquals(
$expected['scope'],
$metadata['scope'],
'shibmd:Scope elements not reflected in parsed metadata'
);
$this->assertEquals(
$expected['UIInfo'],
$metadata['UIInfo'],
'mdui:UIInfo elements not reflected in parsed metadata'
);
$this->assertEquals(
$expected['DiscoHints'],
$metadata['DiscoHints'],
'mdui:DiscoHints elements not reflected in parsed metadata'
);
$this->assertEquals($expected['name'], $metadata['name']);
}