Skip to content

Commit

Permalink
Refactor Roledescriptor using ExtensionPointInterface
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed May 19, 2023
1 parent 399084c commit c1bd856
Show file tree
Hide file tree
Showing 22 changed files with 891 additions and 326 deletions.
1 change: 1 addition & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ jobs:
run: phpcs

- name: Psalm
continue-on-error: true
run: |
psalm -c psalm.xml \
--show-info=true \
Expand Down
39 changes: 39 additions & 0 deletions 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This is a combination of 2 commits.
# This is the 1st commit message:

Refactor Roledescriptor using ExtensionPointInterface

# This is the commit message #2:

#efactor Roledescriptor using ExtensionPointInterface

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Fri May 19 22:23:01 2023 +0200
#
# interactive rebase in progress; onto 399084cb
# Last commands done (2 commands done):
# pick a6a6eeff Refactor Roledescriptor using ExtensionPointInterface
# squash 575a8748 Refactor Roledescriptor using ExtensionPointInterface
# No commands remaining.
# You are currently rebasing branch 'roledescriptor3' on '399084cb'.
#
# Changes to be committed:
# modified: src/SAML2/XML/md/AbstractRoleDescriptor.php
# new file: src/SAML2/XML/md/AbstractRoleDescriptorType.php
# modified: src/SAML2/XML/md/AbstractSSODescriptor.php
# modified: src/SAML2/XML/md/AbstractSignedMdElement.php
# modified: src/SAML2/XML/md/AttributeAuthorityDescriptor.php
# modified: src/SAML2/XML/md/AuthnAuthorityDescriptor.php
# modified: src/SAML2/XML/md/EntityDescriptor.php
# modified: src/SAML2/XML/md/PDPDescriptor.php
# modified: src/SAML2/XML/md/UnknownRoleDescriptor.php
# modified: tests/SAML2/CustomBaseID.php
# new file: tests/SAML2/CustomRoleDescriptor.php
# modified: tests/SAML2/XML/md/EntityDescriptorTest.php
# new file: tests/SAML2/XML/md/RoleDescriptorTest.php
# deleted: tests/SAML2/XML/md/UnknownRoleDescriptorTest.php
# modified: tests/resources/schemas/simplesamlphp.xsd
# renamed: tests/resources/xml/md_UnknownRoleDescriptor.xml -> tests/resources/xml/md_RoleDescriptor.xml
#
198 changes: 110 additions & 88 deletions src/SAML2/XML/md/AbstractRoleDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,37 @@
use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML2\Constants as C;
use SimpleSAML\SAML2\Utils;
use SimpleSAML\SAML2\XML\ExtensionPointInterface;
use SimpleSAML\SAML2\XML\ExtensionPointTrait;
use SimpleSAML\XML\Attribute as XMLAttribute;
use SimpleSAML\XML\Chunk;
use SimpleSAML\XML\Exception\InvalidDOMElementException;
use SimpleSAML\XML\Exception\SchemaViolationException;
use SimpleSAML\XML\ExtendableAttributesTrait;
use SimpleSAML\XML\Exception\TooManyElementsException;
use SimpleSAML\XML\Utils as XMLUtils;

use function array_pop;
use function count;
use function implode;

/**
* Class representing SAML 2 RoleDescriptor element.
* Class representing a SAML2 RoleDescriptor element.
*
* @package simplesamlphp/saml2
*/
abstract class AbstractRoleDescriptor extends AbstractMetadataDocument
abstract class AbstractRoleDescriptor extends AbstractRoleDescriptorType implements ExtensionPointInterface
{
use ExtendableAttributesTrait;
use ExtensionPointTrait;

/** The namespace-attribute for the xs:anyAttribute element */
public const XS_ANY_ATTR_NAMESPACE = C::XS_ANY_NS_OTHER;
/** @var string */
public const LOCALNAME = 'RoleDescriptor';


/**
* Initialize a RoleDescriptor.
* Initialize a md:RoleDescriptor from scratch.
*
* @param string $type
* @param string[] $protocolSupportEnumeration A set of URI specifying the protocols supported.
* @param string|null $ID The ID for this document. Defaults to null.
* @param int|null $validUntil Unix time of validity for this document. Defaults to null.
Expand All @@ -43,124 +53,136 @@ abstract class AbstractRoleDescriptor extends AbstractMetadataDocument
* @param list<\SimpleSAML\XML\Attribute> $namespacedAttributes
*/
public function __construct(
protected array $protocolSupportEnumeration,
protected string $type,
array $protocolSupportEnumeration,
?string $ID = null,
?int $validUntil = null,
?string $cacheDuration = null,
?Extensions $extensions = null,
protected ?string $errorURL = null,
protected array $keyDescriptor = [],
protected ?Organization $organization = null,
protected array $contactPerson = [],
?string $errorURL = null,
array $keyDescriptor = [],
?Organization $organization = null,
array $contactPerson = [],
array $namespacedAttributes = [],
) {
Assert::nullOrValidURI($errorURL, SchemaViolationException::class); // Covers the empty string
Assert::minCount(
parent::__construct(
$protocolSupportEnumeration,
1,
'At least one protocol must be supported by this md:' . static::getLocalName() . '.',
);
Assert::allValidURI($protocolSupportEnumeration, SchemaViolationException::class);
Assert::allIsInstanceOf(
$contactPerson,
ContactPerson::class,
'All contacts must be an instance of md:ContactPerson',
);
Assert::allIsInstanceOf(
$ID,
$validUntil,
$cacheDuration,
$extensions,
$errorURL,
$keyDescriptor,
KeyDescriptor::class,
'All key descriptors must be an instance of md:KeyDescriptor',
$organization,
$contactPerson,
$namespacedAttributes
);

parent::__construct($ID, $validUntil, $cacheDuration, $extensions);

$this->setAttributesNS($namespacedAttributes);
}


/**
* Collect the value of the errorURL property.
*
* @return string|null
*/
public function getErrorURL()
{
return $this->errorURL;
}


/**
* Collect the value of the protocolSupportEnumeration property.
* Return the xsi:type value corresponding this element.
*
* @return string[]
* @return string
*/
public function getProtocolSupportEnumeration()
public function getXsiType(): string
{
return $this->protocolSupportEnumeration;
return $this->type;
}


/**
* Collect the value of the Organization property.
* Convert XML into an RoleDescriptor
* @param \DOMElement $xml The XML element we should load
* @return static
*
* @return \SimpleSAML\SAML2\XML\md\Organization|null
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
* if the qualified name of the supplied element is wrong
*/
public function getOrganization()
public static function fromXML(DOMElement $xml): static
{
return $this->organization;
}
Assert::same($xml->localName, 'RoleDescriptor', InvalidDOMElementException::class);
Assert::same($xml->namespaceURI, C::NS_MD, InvalidDOMElementException::class);
Assert::true(
$xml->hasAttributeNS(C::NS_XSI, 'type'),
'Missing required xsi:type in <saml:RoleDescriptor> element.',
SchemaViolationException::class
);

$type = $xml->getAttributeNS(C::NS_XSI, 'type');
Assert::validQName($type, SchemaViolationException::class);

/**
* Collect the value of the ContactPersons property.
*
* @return \SimpleSAML\SAML2\XML\md\ContactPerson[]
*/
public function getContactPerson()
{
return $this->contactPerson;
}
// first, try to resolve the type to a full namespaced version
$qname = explode(':', $type, 2);
if (count($qname) === 2) {
list($prefix, $element) = $qname;
} else {
$prefix = null;
list($element) = $qname;
}
$ns = $xml->lookupNamespaceUri($prefix);
$type = ($ns === null) ? $element : implode(':', [$ns, $element]);

// now check if we have a handler registered for it
$handler = Utils::getContainer()->getExtensionHandler($type);
if ($handler === null) {
// we don't have a handler, proceed with unknown RoleDescriptor
$protocols = self::getAttribute($xml, 'protocolSupportEnumeration');

$validUntil = self::getOptionalAttribute($xml, 'validUntil', null);
$orgs = Organization::getChildrenOfClass($xml);
Assert::maxCount(
$orgs,
1,
'More than one Organization found in this descriptor',
TooManyElementsException::class,
);

$extensions = Extensions::getChildrenOfClass($xml);
Assert::maxCount(
$extensions,
1,
'Only one md:Extensions element is allowed.',
TooManyElementsException::class,
);

return new UnknownRoleDescriptor(
new Chunk($xml),
$type,
preg_split('/[\s]+/', trim($protocols)),
self::getOptionalAttribute($xml, 'ID', null),
$validUntil !== null ? XMLUtils::xsDateTimeToTimestamp($validUntil) : null,
self::getOptionalAttribute($xml, 'cacheDuration', null),
array_pop($extensions),
self::getOptionalAttribute($xml, 'errorURL', null),
KeyDescriptor::getChildrenOfClass($xml),
array_pop($orgs),
ContactPerson::getChildrenOfClass($xml),
);
}

Assert::subclassOf(
$handler,
AbstractRoleDescriptor::class,
'Elements implementing RoleDescriptor must extend \SimpleSAML\SAML2\XML\saml\AbstractRoleDescriptor.',
);

/**
* Collect the value of the KeyDescriptors property.
*
* @return \SimpleSAML\SAML2\XML\md\KeyDescriptor[]
*/
public function getKeyDescriptor()
{
return $this->keyDescriptor;
return $handler::fromXML($xml);
}


/**
* Add this RoleDescriptor to an EntityDescriptor.
* Convert this RoleDescriptor to XML.
*
* @param \DOMElement $parent The EntityDescriptor we should append this endpoint to.
* @return \DOMElement
* @param \DOMElement|null $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this RoleDescriptor.
*/
public function toUnsignedXML(?DOMElement $parent = null): DOMElement
{
$e = parent::toUnsignedXML($parent);
$e->setAttribute('protocolSupportEnumeration', implode(' ', $this->protocolSupportEnumeration));

foreach ($this->getAttributesNS() as $attr) {
$attr->toXML($e);
}

if ($this->getErrorURL() !== null) {
$e->setAttribute('errorURL', $this->getErrorURL());
}

foreach ($this->getKeyDescriptor() as $kd) {
$kd->toXML($e);
}

$this->getOrganization()?->toXML($e);

foreach ($this->getContactPerson() as $cp) {
$cp->toXML($e);
}
$type = new XMLAttribute(C::NS_XSI, 'xsi', 'type', $this->getXsiType());
$type->toXML($e);

return $e;
}
Expand Down
Loading

0 comments on commit c1bd856

Please sign in to comment.