Skip to content

Commit

Permalink
First work
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed May 13, 2024
1 parent f748fcd commit 3c58f60
Show file tree
Hide file tree
Showing 16 changed files with 338 additions and 65 deletions.
76 changes: 22 additions & 54 deletions src/SAML2/Entity/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ public function __construct(
protected readonly bool $signAuthnRequest = false,
protected readonly bool $signLogout = false,
protected readonly bool $validateLogout = true,
// Use with caution - will leave any form of validation up to the implementer
protected readonly bool $bypassResponseValidation = false,
// Use with caution - will leave any form of signature verification or token decryption up to the implementer
protected readonly bool $bypassResponseVerification = false,
// Use with caution - will leave any form of constraint validation up to the implementer
protected readonly bool $bypassConstraintValidation = false,
) {
}

Expand All @@ -81,9 +83,9 @@ public function __construct(
*/
public function receiveResponse(ServerRequestInterface $request): Response
{
$b = Binding::getCurrentBinding($request);
$binding = Binding::getCurrentBinding($request);

if ($b instanceof HTTPArtifact) {
if ($binding instanceof HTTPArtifact) {
if ($this->metadataProvider === null) {
throw new RuntimeException(
"A MetadataProvider is required to use the HTTP-Artifact binding.",
Expand All @@ -94,7 +96,7 @@ public function receiveResponse(ServerRequestInterface $request): Response
);
}

$artifact = $b->receiveArtifact($request);
$artifact = $binding->receiveArtifact($request);
$this->idpMetadata = $this->metadataProvider->getIdPMetadataForSha1($artifact->getSourceId());

if ($this->idpMetadata === null) {
Expand All @@ -104,15 +106,15 @@ public function receiveResponse(ServerRequestInterface $request): Response
));
}

$b->setIdpMetadata($this->idpMetadata);
$b->setSPMetadata($this->spMetadata);
$binding->setIdpMetadata($this->idpMetadata);
$binding->setSPMetadata($this->spMetadata);
}

$rawResponse = $b->receive($request);
$rawResponse = $binding->receive($request);
Assert::isInstanceOf($rawResponse, Response::class, ResourceNotRecognizedException::class); // Wrong type if msg

// Will return a raw Response prior to any form of verification
if ($this->bypassResponseValidation === true) {
if ($this->bypassResponseVerification === true) {
return $rawResponse;
}

Expand Down Expand Up @@ -156,11 +158,18 @@ public function receiveResponse(ServerRequestInterface $request): Response
$this->idpMetadata = $this->metadataProvider->getIdPMetadata($issuer);
if ($this->idpMetadata === null) {
throw new MetadataNotFoundException(sprintf(
'No metadata found for remote identity provider with SHA1 ID: %s',
'No metadata found for remote identity provider with entityID: %s',
$issuer,
));
}

$responseValidator = createResponseValidator(
$this->idpMetadata,
$this->spMetadata,
$binding,
);
$responseValidator->validate($verifiedResponse);

/**
* See paragraph 6.2 of the SAML 2.0 core specifications for the applicable processing rules
*
Expand Down Expand Up @@ -226,18 +235,11 @@ public function receiveResponse(ServerRequestInterface $request): Response
$verifiedAssertions,
);

// Validate that the destination matches the appropriate endpoint from the SP-metadata
$this->validateResponseDestination($b, $decryptedResponse);

// TODO: Refactor response- and assertion-validators and run them here
// Validate that the issuer matches an entity we know
if (!($b instanceof HTTPArtifact)) {
$idpMetadata = $this->metadataProvider->getIdPMetadata($decryptedResponse->getEntityId());
// Will return a verified and fully decrypted Response prior to any form of validation
if ($this->bypassConstraintValidation === true) {
return $decryptedResponse;
}

// Validate that the status is 'success'
$this->validateResponseStatus($decryptedResponse);

// TODO: Validate assertions

return $decryptedResponse;
Expand Down Expand Up @@ -276,40 +278,6 @@ protected function decryptElement(EncryptedElementInterface $element): Encryptab
}


/**
* Validate the status of the received response.
*
* @param \SimpleSAML\SAML2\XML\samlp\Response $response
*/
protected function validateResponseStatus(Response $response): void
{
if (!$response->isSuccess()) {
throw new RemoteException($response->getStatus());
}
}


/**
* Validate the destination of the received response.
*
* @param \SimpleSAML\SAML2\Binding $binding
* @param \SimpleSAML\SAML2\XML\samlp\Response $response
* @throws \SimpleSAML\SAML2\Exception\DestinationMismatchException
*/
protected function validateResponseDestination(Binding $b, Response $response): void
{
foreach ($this->spMetadata->getAssertionConsumerService() as $assertionConsumerService) {
if ($assertionConsumerService->getLocation() === $response->getDestination()) {
if (Binding::getBinding($assertionConsumerService->getBinding()) instanceof $b) {
return;
}
}
}

throw new ResourceNotRecognizedException();
}


/**
* Verify the signature of an element using the available validation keys.
*
Expand Down
12 changes: 12 additions & 0 deletions src/SAML2/Exception/ConstraintViolationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Exception;

/**
* Exception to be raised when validation of a constraint fails.
*/
class ConstraintViolationException extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/SAML2/HTTPArtifact.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public function receiveArtifact(ServerRequestInterface $request): Artifact
*/
public function receive(ServerRequestInterface $request): AbstractMessage
{
$idpMetadata = $this->idPMetadata;
$idpMetadata = $this->idpMetadata;

$endpoint = null;
foreach ($idpMetadata->getAssertionConsumerService() as $assertionConsumerService) {
Expand Down
16 changes: 8 additions & 8 deletions src/SAML2/Metadata/AbstractProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ abstract class AbstractProvider
*/
protected function __construct(
protected string $entityId,
protected EncryptionAlgorithmFactory|KeyTransportAlgorithmFactory|null $encryptionAlgorithmFactory = null,
protected SignatureAlgorithmFactory|null $signatureAlgorithmFactory = null,
protected string $signatureAlgorithm = C::SIG_RSA_SHA256,
protected array $validatingKeys = [],
protected PrivateKey|null $signingKey = null,
protected PublicKey|SymmetricKey|null $encryptionKey = null,
protected array $decryptionKeys = [],
protected array $IDPList = [],
protected EncryptionAlgorithmFactory|KeyTransportAlgorithmFactory|null $encryptionAlgorithmFactory,
protected SignatureAlgorithmFactory|null $signatureAlgorithmFactory,
protected string $signatureAlgorithm,
protected array $validatingKeys,
protected PrivateKey|null $signingKey,
protected PublicKey|SymmetricKey|null $encryptionKey,
protected array $decryptionKeys,
protected array $IDPList,
) {
Assert::validURI($entityId);
Assert::validURI($signatureAlgorithm);
Expand Down
2 changes: 1 addition & 1 deletion src/SAML2/Metadata/IdentityProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public function __construct(
$entityId,
$encryptionAlgorithmFactory,
$signatureAlgorithmFactory,
$signingKey,
$signatureAlgorithm,
$validatingKeys,
$signingKey,
$encryptionKey,
$decryptionKeys,
$IDPList,
Expand Down
2 changes: 1 addition & 1 deletion src/SAML2/Metadata/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public function __construct(
$entityId,
$encryptionAlgorithmFactory,
$signatureAlgorithmFactory,
$signingKey,
$signatureAlgorithm,
$validatingKeys,
$signingKey,
$encryptionKey,
$decryptionKeys,
$IDPList,
Expand Down
15 changes: 15 additions & 0 deletions src/SAML2/Process/IdentityProviderAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process;

use SimpleSAML\SAML2\Metadata;

interface IdentityProviderAwareInterface
{
/**
* Set the IdP metadata.
*/
public function setIdPMetadata(Metadata\IdentityProvider $idpMetadata): void;
}
20 changes: 20 additions & 0 deletions src/SAML2/Process/IdentityProviderAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process;

use SimpleSAML\SAML2\Metadata;

trait IdentityProviderAwareTrait
{
protected Metadata\IdentityProvider $idpMetadata;

/**
* Set the IdP metadata.
*/
public function setIdPMetadata(Metadata\IdentityProvider $idpMetadata): void
{
$this->idpMetadata = $idpMetadata;
}
}
15 changes: 15 additions & 0 deletions src/SAML2/Process/ServiceProviderAwareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process;

use SimpleSAML\SAML2\Metadata;

interface ServiceProviderAwareInterface
{
/**
* Set the SP metadata.
*/
public function setSPMetadata(Metadata\ServiceProvider $spMetadata): void;
}
20 changes: 20 additions & 0 deletions src/SAML2/Process/ServiceProviderAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process;

use SimpleSAML\SAML2\Metadata;

trait ServiceProviderAwareTrait
{
protected Metadata\ServiceProvider $spMetadata;

/**
* Set the SP metadata.
*/
public function setSPMetadata(Metadata\ServiceProvider $spMetadata): void
{
$this->spMetadata = $spMetadata;
}
}
17 changes: 17 additions & 0 deletions src/SAML2/Process/Validation/ConstraintValidatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Validation;

interface ConstraintValidatorInterface
{
/**
* Runs the validation.
*
* If this function returns, the validation has been succesful.
*
* @throws \SimpleSAML\SAML2\Exception\ConstraintViolationException when the condition fails.
*/
public function validate(): void;
}
43 changes: 43 additions & 0 deletions src/SAML2/Process/Validation/Response/DestinationMatches.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process\Validation\Response;

use SimpleSAML\SAML2\{Binding, Metadata};
use SimpleSAML\SAML2\Process\{ServiceProviderAwareInterface, ServiceProviderAwareTrait};
use SimpleSAML\SAML2\Process\Response\ConstraintValidatorInterface;
use SimpleSAML\SAML2\XML\samlp\Response;

final class DestinationMatches implements ConstraintValidatorInterface
{
/**
* DestinationMatches constructor.
*
* @param \SimpleSAML\SAML2\Metadata\ServiceProvider $spMetadata
* @param \SimpleSAML\SAML2\Binding $binding
*/
public function __construct(
private Metadata\ServiceProvider $spMetadata,
private Binding $binding,
) {
}


/**
* @param \SimpleSAML\SAML2\XML\samlp\Response $response
*/
public function validate(Response $response): void
{
// Validate that the destination matches the appropriate endpoint from the SP-metadata
foreach ($this->spMetadata->getAssertionConsumerService() as $assertionConsumerService) {
if ($assertionConsumerService->getLocation() === $response->getDestination()) {
if (Binding::getBinding($assertionConsumerService->getBinding()) instanceof $this->binding) {
return;
}
}
}

throw new ResourceNotRecognizedException();
}
}
42 changes: 42 additions & 0 deletions src/SAML2/Process/Validation/ResponseValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Process\Validation;

use SimpleSAML\SAML2\{Binding, Metadata};
use SimpleSAML\SAML2\Process\Validation\Response\{DestinationMatches, IsSuccessful};

class ResponseValidator implements ValidatorInterface
{
use ValidatorTrait;

/**
* @param \SimpleSAML\SAML2\Metadata\IdentityProvider The IdP-metadata
* @param \SimpleSAML\SAML2\Metadata\ServiceProvider The SP-metadata
*/
private function __construct()
{
protected Metadata\IdentityProvider $idpMetadata,
protected Metadata\ServiceProvider $spMetadata,
}


/**
* @param \SimpleSAML\SAML2\Metadata\IdentityProvider $idpMetadata
* @param \SimpleSAML\SAML2\Metadata\ServiceProvider $spMetadata
* @param string $binding
* @return \SimpleSAML\SAML2\Validation\ResponseValidator
*/
public static function createResponseValidator(
Metadata\IdentityProvider $idpMetadata,
Metadata\ServiceProvider $spMetadata,
Binding $binding,
): ResponseValidator {
$validator = new self();
$validator->addConstraintValidator(new DestinationMatches($binding));
$validator->addConstraintValidator(new IsSuccesful());

return $validator;
}
}
Loading

0 comments on commit 3c58f60

Please sign in to comment.