Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions classes/element.registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
'http://eduid.cz/schema/metadata/1.0' => [
'RepublishRequest' => '\SimpleSAML\SAML2\XML\emd\RepublishRequest',
],
'urn:mace:surf.nl:stepup:gssp-extensions' => [
'UserAttributes' => '\SimpleSAML\SAML2\XML\gssp\UserAttributes',
],
'urn:oasis:names:tc:SAML:2.0:metadata' => [
'AdditionalMetadataLocation' => '\SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation',
'AffiliateMember' => '\SimpleSAML\SAML2\XML\md\AffiliateMember',
Expand Down
24 changes: 24 additions & 0 deletions resources/schemas/gssp-extensions.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:mace:surf.nl:stepup:gssp-extensions"
xmlns:gssp="urn:mace:surf.nl:stepup:gssp-extensions"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
elementFormDefault="unqualified"
attributeFormDefault="unqualified"
version="1.0">
<xs:import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
schemaLocation="saml-schema-assertion-2.0.xsd"/>
<xs:annotation>
<xs:documentation>
SAML Extension for use in the GSSP protocol: https://github.com/OpenConext/Stepup-Gateway/blob/develop/docs/GSSP.md
Version 1.0:
- Define UserAttributes element for use in AuthnRequest during GSSP enrollemnt
</xs:documentation>
</xs:annotation>
<xs:element name="UserAttributes" type="gssp:UserAttributesType"/>
<xs:complexType name="UserAttributesType">
<xs:sequence>
<xs:element ref="saml:Attribute" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
5 changes: 5 additions & 0 deletions src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ class Constants extends \SimpleSAML\XMLSecurity\Constants
*/
public const string NS_EMD = 'http://eduid.cz/schema/metadata/1.0';

/**
* The namespace for the GSSP-extensions.
*/
public const string NS_GSSP = 'urn:mace:surf.nl:stepup:gssp-extensions';

/**
* The namespace for the SAML 2 HoK Web Browser SSO Profile.
*/
Expand Down
22 changes: 22 additions & 0 deletions src/XML/gssp/AbstractGsspElement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\XML\gssp;

use SimpleSAML\SAML2\Constants as C;
use SimpleSAML\XML\AbstractElement;

/**
* Abstract class to be implemented by all the classes in this namespace
*
* @package simplesamlphp/saml2
*/
abstract class AbstractGsspElement extends AbstractElement
{
public const string NS = C::NS_GSSP;

public const string NS_PREFIX = 'gssp';

public const string SCHEMA = 'resources/schemas/gssp-extensions.xsd';
}
77 changes: 77 additions & 0 deletions src/XML/gssp/UserAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\XML\gssp;

use Dom;
use SimpleSAML\SAML2\Assert\Assert;
use SimpleSAML\SAML2\XML\saml\Attribute;
use SimpleSAML\XML\Constants as C;
use SimpleSAML\XML\SchemaValidatableElementInterface;
use SimpleSAML\XML\SchemaValidatableElementTrait;
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
use SimpleSAML\XMLSchema\Exception\MissingElementException;

/**
* Class representing a GSSP UserAttributes-element
*
* @package simplesamlphp/saml2
*/
class UserAttributes extends AbstractGsspElement implements SchemaValidatableElementInterface
{
use SchemaValidatableElementTrait;


/**
* UserAttributes constructor.
*
* @param \SimpleSAML\SAML2\XML\saml\Attribute[] $attributes
*/
final public function __construct(
protected array $attributes = [],
) {
Assert::minCount($attributes, 1, MissingElementException::class);
Assert::maxCount($attributes, C::UNBOUNDED_LIMIT);
Assert::allIsInstanceOf($attributes, Attribute::class);
}


/**
* @return \SimpleSAML\SAML2\XML\saml\Attribute[]
*/
public function getAttributes(): array
{
return $this->attributes;
}


/**
* @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
* if the qualified name of the supplied element is wrong
*/
public static function fromXML(Dom\Element $xml): static
{
Assert::same($xml->localName, 'UserAttributes', InvalidDOMElementException::class);
Assert::same($xml->namespaceURI, UserAttributes::NS, InvalidDOMElementException::class);

return new static(
Attribute::getChildrenOfClass($xml),
);
}


/**
* Convert this UserAttributes to XML.
*/
public function toXML(?Dom\Element $parent = null): Dom\Element
{
$e = $this->instantiateParentElement($parent);

foreach ($this->getAttributes() as $attribute) {
$attribute->toXML($e);
}

return $e;
}
}
106 changes: 106 additions & 0 deletions tests/SAML2/XML/gssp/UserAttributesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\Test\SAML2\XML\gssp;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use SimpleSAML\SAML2\Constants as C;
use SimpleSAML\SAML2\Type\SAMLAnyURIValue;
use SimpleSAML\SAML2\Type\SAMLStringValue;
use SimpleSAML\SAML2\XML\gssp\AbstractGsspElement;
use SimpleSAML\SAML2\XML\gssp\UserAttributes;
use SimpleSAML\SAML2\XML\saml\Attribute;
use SimpleSAML\SAML2\XML\saml\AttributeValue;
use SimpleSAML\XML\DOMDocumentFactory;
use SimpleSAML\XML\TestUtils\SchemaValidationTestTrait;
use SimpleSAML\XML\TestUtils\SerializableElementTestTrait;
use SimpleSAML\XMLSchema\Exception\MissingElementException;

use function dirname;
use function strval;

/**
* Class \SimpleSAML\SAML2\XML\gssp\UserAttributesTest
*
* @package simplesamlphp/saml2
*/
#[Group('gssp')]
#[CoversClass(UserAttributes::class)]
#[CoversClass(AbstractGsspElement::class)]
final class UserAttributesTest extends TestCase
{
use SchemaValidationTestTrait;
use SerializableElementTestTrait;


/**
*/
public static function setUpBeforeClass(): void
{
self::$testedClass = UserAttributes::class;

self::$xmlRepresentation = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/gssp_UserAttributes.xml',
);
}


// marshalling


/**
*/
public function testMarshalling(): void
{
$userAttributes = new UserAttributes(
[
new Attribute(
name: SAMLStringValue::fromString('urn:mace:dir:attribute-def:mail'),
nameFormat: SAMLAnyURIValue::fromString(C::NAMEFORMAT_URI),
attributeValue: [
new AttributeValue(SAMLStringValue::fromString('user@example.com')),
],
),
],
);

$expectedXml = self::$xmlRepresentation->saveXml(self::$xmlRepresentation->documentElement);
$this->assertNotFalse($expectedXml);
$actualXml = strval($userAttributes);

$this->assertXmlStringEqualsXmlString($expectedXml, $actualXml);
}


/**
*/
public function testMarshallingMissingAttributesThrowsException(): void
{
$this->expectException(MissingElementException::class);

new UserAttributes([]);
}


// unmarshalling


/**
*/
public function testUnmarshallingMissingAttributesThrowsException(): void
{
$document = DOMDocumentFactory::fromString(
<<<XML
<saml:UserAttributes xmlns:saml="urn:mace:surf.nl:stepup:gssp-extensions">
</saml:UserAttributes>
XML
,
);

$this->expectException(MissingElementException::class);
UserAttributes::fromXML($document->documentElement);
}
}
26 changes: 21 additions & 5 deletions tests/SAML2/XML/samlp/AuthnRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
use SimpleSAML\SAML2\Type\SAMLStringValue;
use SimpleSAML\SAML2\Utils;
use SimpleSAML\SAML2\Utils\XPath;
use SimpleSAML\SAML2\XML\aslo\Asynchronous;
use SimpleSAML\SAML2\XML\gssp\UserAttributes;
use SimpleSAML\SAML2\XML\saml\Attribute;
use SimpleSAML\SAML2\XML\saml\AttributeValue;
use SimpleSAML\SAML2\XML\saml\Audience;
use SimpleSAML\SAML2\XML\saml\AudienceRestriction;
use SimpleSAML\SAML2\XML\saml\AuthnContextClassRef;
Expand Down Expand Up @@ -104,8 +106,15 @@ public function testMarshalling(): void
);

$extensions = new Extensions([
// Note: this makes zero sense in an AuthnRequest, but we can't be picky in what extensions we accept
new Asynchronous(),
new UserAttributes([
new Attribute(
name: SAMLStringValue::fromString('urn:mace:dir:attribute-def:mail'),
nameFormat: SAMLAnyURIValue::fromString(C::NAMEFORMAT_URI),
attributeValue: [
new AttributeValue(SAMLStringValue::fromString('user@example.com')),
],
),
]),
]);

$authnRequest = new AuthnRequest(
Expand Down Expand Up @@ -191,8 +200,15 @@ public function testMarshallingElementOrdering(): void

// Create Extensions
$extensions = new Extensions([
// Note: this makes zero sense in an AuthnRequest, but we can't be picky in what extensions we accept
new Asynchronous(),
new UserAttributes([
new Attribute(
name: SAMLStringValue::fromString('urn:mace:dir:attribute-def:mail'),
nameFormat: SAMLAnyURIValue::fromString(C::NAMEFORMAT_URI),
attributeValue: [
new AttributeValue(SAMLStringValue::fromString('user@example.com')),
],
),
]),
]);

$authnRequest = new AuthnRequest(
Expand Down
5 changes: 5 additions & 0 deletions tests/resources/xml/gssp_UserAttributes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<gssp:UserAttributes xmlns:gssp="urn:mace:surf.nl:stepup:gssp-extensions" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:Attribute Name="urn:mace:dir:attribute-def:mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>user@example.com</saml:AttributeValue>
</saml:Attribute>
</gssp:UserAttributes>
8 changes: 6 additions & 2 deletions tests/resources/xml/samlp_AuthnRequest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:aslo="urn:oasis:names:tc:SAML:2.0:protocol:ext:async-slo" Version="2.0" ID="_2b0226190ca1c22de6f66e85f5c95158" IssueInstant="2014-09-22T13:42:00Z" Destination="https://tiqr.stepup.org/idp/profile/saml2/Redirect/SSO" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified">
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:gssp="urn:mace:surf.nl:stepup:gssp-extensions" Version="2.0" ID="_2b0226190ca1c22de6f66e85f5c95158" IssueInstant="2014-09-22T13:42:00Z" Destination="https://tiqr.stepup.org/idp/profile/saml2/Redirect/SSO" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified">
<saml:Issuer>https://gateway.stepup.org/saml20/sp/metadata</saml:Issuer>
<samlp:Extensions>
<aslo:Asynchronous/>
<gssp:UserAttributes>
<saml:Attribute Name="urn:mace:dir:attribute-def:mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>user@example.com</saml:AttributeValue>
</saml:Attribute>
</gssp:UserAttributes>
</samlp:Extensions>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">user@example.org</saml:NameID>
Expand Down
Loading