Skip to content

Commit

Permalink
Implement saml:Condition
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed Feb 27, 2024
1 parent b2ab815 commit 5d99a53
Show file tree
Hide file tree
Showing 6 changed files with 397 additions and 0 deletions.
123 changes: 123 additions & 0 deletions src/SAML11/XML/saml/AbstractCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML\saml;

use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\Constants as C;
use SimpleSAML\SAML11\Utils;
use SimpleSAML\SAML11\XML\ExtensionPointInterface;
use SimpleSAML\SAML11\XML\ExtensionPointTrait;
use SimpleSAML\XML\Attribute as XMLAttribute;
use SimpleSAML\XML\Chunk;
use SimpleSAML\XML\Exception\InvalidDOMElementException;
use SimpleSAML\XML\Exception\SchemaViolationException;

use function count;
use function explode;

/**
* SAML Condition data type.
*
* @package simplesamlphp/saml11
*/
abstract class AbstractCondition extends AbstractConditionType implements ExtensionPointInterface
{
use ExtensionPointTrait;

/** @var string */
public const LOCALNAME = 'Condition';


/**
* Initialize a custom saml:Condition element.
*
* @param string $type
*/
protected function __construct(
protected string $type,
) {
}


/**
* @inheritDoc
*/
public function getXsiType(): string
{
return $this->type;
}


/**
* Convert an XML element into a Condition.
*
* @param \DOMElement $xml The root XML element
* @return static
*
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
* if the qualified name of the supplied element is wrong
*/
public static function fromXML(DOMElement $xml): static
{
Assert::same($xml->localName, 'Condition', InvalidDOMElementException::class);
Assert::same($xml->namespaceURI, C::NS_SAML, InvalidDOMElementException::class);
Assert::true(
$xml->hasAttributeNS(C::NS_XSI, 'type'),
'Missing required xsi:type in <saml:Condition> element.',
SchemaViolationException::class,
);

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

// 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 condition
return new UnknownCondition(new Chunk($xml), $type);
}

Assert::subclassOf(
$handler,
AbstractCondition::class,
'Elements implementing Condition must extend \SimpleSAML\SAML11\XML\saml\AbstractCondition.',
);
return $handler::fromXML($xml);
}


/**
* Convert this Condition to XML.
*
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this Condition.
*/
public function toXML(DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);
$e->setAttributeNS(
'http://www.w3.org/2000/xmlns/',
'xmlns:' . static::getXsiTypePrefix(),
static::getXsiTypeNamespaceURI(),
);

$type = new XMLAttribute(C::NS_XSI, 'xsi', 'type', $this->getXsiType());
$type->toXML($e);

return $e;
}
}
14 changes: 14 additions & 0 deletions src/SAML11/XML/saml/AbstractConditionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML\saml;

/**
* Abstract class to be implemented by all the conditions in this namespace
*
* @package simplesamlphp/saml11
*/
abstract class AbstractConditionType extends AbstractSamlElement
{
}
50 changes: 50 additions & 0 deletions src/SAML11/XML/saml/UnknownCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML\saml;

use DOMElement;
use SimpleSAML\XML\Chunk;

/**
* Class for unknown conditions.
*
* @package simplesamlphp/saml11
*/
final class UnknownCondition extends AbstractCondition
{
/**
* @param \SimpleSAML\XML\Chunk $chunk The whole Condition element as a chunk object.
* @param string $type The xsi:type of this condition.
*/
public function __construct(
protected Chunk $chunk,
string $type,
) {
parent::__construct($type);
}


/**
* Get the raw version of this condition as a Chunk.
*
* @return \SimpleSAML\XML\Chunk
*/
public function getRawCondition(): Chunk
{
return $this->chunk;
}


/**
* Convert this unknown condition to XML.
*
* @param \DOMElement|null $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this unknown condition.
*/
public function toXML(DOMElement $parent = null): DOMElement
{
return $this->getRawCondition()->toXML($parent);
}
}
3 changes: 3 additions & 0 deletions tests/resources/xml/saml_Condition.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<saml:Condition xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:ssp="urn:x-simplesamlphp:namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ssp:CustomConditionType">
<saml:Audience>urn:some:audience</saml:Audience>
</saml:Condition>
101 changes: 101 additions & 0 deletions tests/src/SAML11/CustomCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\Test\SAML11;

use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\Constants as C;
use SimpleSAML\SAML11\XML\saml\AbstractCondition;
use SimpleSAML\SAML11\XML\saml\Audience;
use SimpleSAML\XML\Exception\InvalidDOMElementException;

/**
* Example class to demonstrate how Condition can be extended.
*
* @package simplesamlphp\saml11
*/
final class CustomCondition extends AbstractCondition
{
/** @var string */
protected const XSI_TYPE_NAME = 'CustomConditionType';

/** @var string */
protected const XSI_TYPE_NAMESPACE = 'urn:x-simplesamlphp:namespace';

/** @var string */
protected const XSI_TYPE_PREFIX = 'ssp';


/**
* CustomCondition constructor.
*
* @param \SimpleSAML\SAML11\XML\saml\Audience[] $audience
*/
public function __construct(
protected array $audience,
) {
Assert::allIsInstanceOf($audience, Audience::class);

parent::__construct(self::XSI_TYPE_PREFIX . ':' . self::XSI_TYPE_NAME);
}


/**
* Get the value of the audience-attribute.
*
* @return \SimpleSAML\SAML11\XML\saml\Audience[]
*/
public function getAudience(): array
{
return $this->audience;
}


/**
* Convert XML into a Condition
*
* @param \DOMElement $xml The XML element we should load
* @return \SimpleSAML\SAML11\XML\saml\AbstractCondition
*
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
* if the qualified name of the supplied element is wrong
*/
public static function fromXML(DOMElement $xml): static
{
Assert::same($xml->localName, 'Condition', InvalidDOMElementException::class);
Assert::notNull($xml->namespaceURI, InvalidDOMElementException::class);
Assert::same($xml->namespaceURI, AbstractCondition::NS, InvalidDOMElementException::class);
Assert::true(
$xml->hasAttributeNS(C::NS_XSI, 'type'),
'Missing required xsi:type in <saml:Condition> element.',
InvalidDOMElementException::class,
);

$type = $xml->getAttributeNS(C::NS_XSI, 'type');
Assert::same($type, self::XSI_TYPE_PREFIX . ':' . self::XSI_TYPE_NAME);

$audience = Audience::getChildrenOfClass($xml);

return new static($audience);
}


/**
* Convert this Condition to XML.
*
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this Condition.
*/
public function toXML(DOMElement $parent = null): DOMElement
{
$e = parent::toXML($parent);

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

return $e;
}
}
Loading

0 comments on commit 5d99a53

Please sign in to comment.