<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Protocol;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\Assertion\Assertion;
use LightSaml\Model\Assertion\EncryptedElement;
use LightSaml\SamlConstants;

class Response extends StatusResponse
{
    /** @var Assertion[] */
    protected $assertions = array();

    /** @var EncryptedElement[] */
    protected $encryptedAssertions = array();

    /**
     * @return Assertion[]
     */
    public function getAllAssertions()
    {
        return $this->assertions;
    }

    /**
     * @return Assertion|null
     */
    public function getFirstAssertion()
    {
        if (is_array($this->assertions) && isset($this->assertions[0])) {
            return $this->assertions[0];
        }

        return null;
    }

    /**
     * @return EncryptedElement[]
     */
    public function getAllEncryptedAssertions()
    {
        return $this->encryptedAssertions;
    }

    /**
     * @return EncryptedElement|null
     */
    public function getFirstEncryptedAssertion()
    {
        if (is_array($this->encryptedAssertions) && isset($this->encryptedAssertions[0])) {
            return $this->encryptedAssertions[0];
        }

        return null;
    }

    /**
     * Returns assertions with <AuthnStatement> and <Subject> with at least one <SubjectConfirmation>
     * element containing a Method of urn:oasis:names:tc:SAML:2.0:cm:bearer.
     *
     * @return \LightSaml\Model\Assertion\Assertion[]
     */
    public function getBearerAssertions()
    {
        $result = array();
        if ($this->getAllAssertions()) {
            foreach ($this->getAllAssertions() as $assertion) {
                if ($assertion->hasBearerSubject()) {
                    $result[] = $assertion;
                }
            } // foreach assertions
        }

        return $result;
    }

    /**
     * @param Assertion $assertion
     *
     * @return Response
     */
    public function addAssertion(Assertion $assertion)
    {
        $this->assertions[] = $assertion;

        return $this;
    }

    /**
     * @param Assertion $removedAssertion
     *
     * @return Response
     */
    public function removeAssertion(Assertion $removedAssertion)
    {
        $arr = array();
        $hasThatAssertion = false;
        foreach ($this->getAllAssertions() as $assertion) {
            if ($assertion !== $removedAssertion) {
                $arr[] = $assertion;
            } else {
                $hasThatAssertion = true;
            }
        }

        if (false === $hasThatAssertion) {
            throw new \InvalidArgumentException('Response does not have assertion specified to be removed');
        }

        return $this;
    }

    /**
     * @param EncryptedElement $encryptedAssertion
     *
     * @return Response
     */
    public function addEncryptedAssertion(EncryptedElement $encryptedAssertion)
    {
        $this->encryptedAssertions[] = $encryptedAssertion;

        return $this;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('samlp:Response', SamlConstants::NS_PROTOCOL, $parent, $context);

        parent::serialize($result, $context);

        $this->manyElementsToXml($this->getAllAssertions(), $result, $context, null);
        $this->manyElementsToXml($this->getAllEncryptedAssertions(), $result, $context, null);

        // must be done here at the end and not in a base class where declared in order to include signing of the elements added here
        $this->singleElementsToXml(array('Signature'), $result, $context);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'Response', SamlConstants::NS_PROTOCOL);

        parent::deserialize($node, $context);

        $this->assertions = array();
        $this->manyElementsFromXml(
            $node,
            $context,
            'Assertion',
            'saml',
            'LightSaml\Model\Assertion\Assertion',
            'addAssertion'
        );

        $this->encryptedAssertions = array();
        $this->manyElementsFromXml(
            $node,
            $context,
            'EncryptedAssertion',
            'saml',
            'LightSaml\Model\Assertion\EncryptedAssertionReader',
            'addEncryptedAssertion'
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\SamlConstants;

class AudienceRestriction extends AbstractCondition
{
    /**
     * @var string[]
     */
    protected $audience = array();

    /**
     * @param string|string[] $audience
     */
    public function __construct($audience = array())
    {
        if (false == is_array($audience)) {
            $audience = array($audience);
        }
        $this->audience = $audience;
    }

    /**
     * @param string $audience
     *
     * @return AudienceRestriction
     */
    public function addAudience($audience)
    {
        $this->audience[] = $audience;

        return $this;
    }

    /**
     * @return string[]
     */
    public function getAllAudience()
    {
        return $this->audience;
    }

    /**
     * @param string $value
     *
     * @return bool
     */
    public function hasAudience($value)
    {
        if (is_array($this->audience)) {
            foreach ($this->audience as $a) {
                if ($a == $value) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('AudienceRestriction', SamlConstants::NS_ASSERTION, $parent, $context);

        $this->manyElementsToXml($this->getAllAudience(), $result, $context, 'Audience', SamlConstants::NS_ASSERTION);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'AudienceRestriction', SamlConstants::NS_ASSERTION);

        $this->audience = array();
        $this->manyElementsFromXml($node, $context, 'Audience', 'saml', null, 'addAudience');
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Validator\Model\Xsd;

class XsdError
{
    const WARNING = 'Warning';
    const ERROR = 'Error';
    const FATAL = 'Fatal';

    private static $levelMap = [
        LIBXML_ERR_WARNING => self::WARNING,
        LIBXML_ERR_ERROR => self::ERROR,
        LIBXML_ERR_FATAL => self::FATAL,
    ];

    /** @var string */
    private $level;

    /** @var string */
    private $code;

    /** @var string */
    private $message;

    /** @var string */
    private $line;

    /** @var string */
    private $column;

    /**
     * @param \LibXMLError $error
     *
     * @return XsdError
     */
    public static function fromLibXMLError(\LibXMLError $error)
    {
        return new self(
            isset(self::$levelMap[$error->level]) ? self::$levelMap[$error->level] : 'Unknown',
            $error->code,
            $error->message,
            $error->line,
            $error->column
        );
    }

    /**
     * @param string $level
     * @param string $code
     * @param string $message
     * @param string $line
     * @param string $column
     */
    public function __construct($level, $code, $message, $line, $column)
    {
        $this->level = $level;
        $this->code = $code;
        $this->message = $message;
        $this->line = $line;
        $this->column = $column;
    }

    /**
     * @return string
     */
    public function getLevel()
    {
        return $this->level;
    }

    /**
     * @return string
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * @return string
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @return string
     */
    public function getLine()
    {
        return $this->line;
    }

    /**
     * @return string
     */
    public function getColumn()
    {
        return $this->column;
    }

    public function __toString()
    {
        return sprintf(
            '%s %s: %s on line %s column %s',
            $this->level,
            $this->code,
            trim($this->message),
            $this->line,
            $this->column
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Metadata;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\SamlConstants;

class SpSsoDescriptor extends SSODescriptor
{
    /** @var bool|null */
    protected $authnRequestsSigned;

    /** @var bool|null */
    protected $wantAssertionsSigned;

    /** @var AssertionConsumerService[]|null */
    protected $assertionConsumerServices;

    /**
     * @param AssertionConsumerService $assertionConsumerService
     *
     * @return SpSsoDescriptor
     */
    public function addAssertionConsumerService(AssertionConsumerService $assertionConsumerService)
    {
        if (false == is_array($this->assertionConsumerServices)) {
            $this->assertionConsumerServices = array();
        }
        if (null === $assertionConsumerService->getIndex()) {
            $assertionConsumerService->setIndex(count($this->assertionConsumerServices));
        }
        $this->assertionConsumerServices[] = $assertionConsumerService;

        return $this;
    }

    /**
     * @return AssertionConsumerService[]|null
     */
    public function getAllAssertionConsumerServices()
    {
        return $this->assertionConsumerServices;
    }

    /**
     * @param string $binding
     *
     * @return AssertionConsumerService[]
     */
    public function getAllAssertionConsumerServicesByBinding($binding)
    {
        $result = array();
        foreach ($this->getAllAssertionConsumerServices() as $svc) {
            if ($svc->getBinding() == $binding) {
                $result[] = $svc;
            }
        }

        return $result;
    }

    /**
     * @param string $url
     *
     * @return AssertionConsumerService[]
     */
    public function getAllAssertionConsumerServicesByUrl($url)
    {
        $result = array();
        foreach ($this->getAllAssertionConsumerServices() as $svc) {
            if ($svc->getLocation() == $url) {
                $result[] = $svc;
            }
        }

        return $result;
    }

    /**
     * @param int $index
     *
     * @return AssertionConsumerService|null
     */
    public function getAssertionConsumerServicesByIndex($index)
    {
        foreach ($this->getAllAssertionConsumerServices() as $svc) {
            if ($svc->getIndex() == $index) {
                return $svc;
            }
        }

        return null;
    }

    /**
     * @param string|null $binding
     *
     * @return AssertionConsumerService|null
     */
    public function getFirstAssertionConsumerService($binding = null)
    {
        foreach ($this->getAllAssertionConsumerServices() as $svc) {
            if (null == $binding || $svc->getBinding() == $binding) {
                return $svc;
            }
        }

        return null;
    }

    /**
     * @param bool|null $authnRequestsSigned
     *
     * @return SpSsoDescriptor
     */
    public function setAuthnRequestsSigned($authnRequestsSigned)
    {
        $this->authnRequestsSigned = filter_var($authnRequestsSigned, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]);

        return $this;
    }

    /**
     * @return bool|null
     */
    public function getAuthnRequestsSigned()
    {
        return $this->authnRequestsSigned;
    }

    /**
     * @param bool|null $wantAssertionsSigned
     *
     * @return SpSsoDescriptor
     */
    public function setWantAssertionsSigned($wantAssertionsSigned)
    {
        $this->wantAssertionsSigned = filter_var($wantAssertionsSigned, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]);

        return $this;
    }

    /**
     * @return bool|null
     */
    public function getWantAssertionsSigned()
    {
        return $this->wantAssertionsSigned;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('SPSSODescriptor', SamlConstants::NS_METADATA, $parent, $context);

        parent::serialize($result, $context);

        $this->attributesToXml(array('AuthnRequestsSigned', 'WantAssertionsSigned'), $result);

        $this->manyElementsToXml($this->getAllAssertionConsumerServices(), $result, $context, null);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'SPSSODescriptor', SamlConstants::NS_METADATA);

        parent::deserialize($node, $context);

        $this->attributesFromXml($node, array('AuthnRequestsSigned', 'WantAssertionsSigned'));

        $this->manyElementsFromXml(
            $node,
            $context,
            'AssertionConsumerService',
            'md',
            'LightSaml\Model\Metadata\AssertionConsumerService',
            'addAssertionConsumerService'
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\Request;

use LightSaml\State\Request\RequestState;

abstract class AbstractRequestStateArrayStore implements RequestStateStoreInterface
{
    /**
     * @param RequestState $state
     *
     * @return AbstractRequestStateArrayStore
     */
    public function set(RequestState $state)
    {
        $arr = $this->getArray();
        $arr[$state->getId()] = $state;
        $this->setArray($arr);

        return $this;
    }

    /**
     * @param string $id
     *
     * @return RequestState|null
     */
    public function get($id)
    {
        $result = null;
        $arr = $this->getArray();
        if (false == is_array($arr)) {
            $arr = array();
            $this->setArray($arr);
        }
        if (isset($arr[$id])) {
            $result = $arr[$id];
        }
        if ($result instanceof RequestState) {
            return $result;
        }

        return null;
    }

    /**
     * @param string $id
     *
     * @return bool
     */
    public function remove($id)
    {
        $arr = $this->getArray();
        $result = isset($arr[$id]);
        unset($arr[$id]);
        $this->setArray($arr);

        return $result;
    }

    /**
     * @return void
     */
    public function clear()
    {
        $this->setArray(array());
    }

    /**
     * @return array
     */
    abstract protected function getArray();

    /**
     * @param array $arr
     *
     * @return AbstractRequestStateArrayStore
     */
    abstract protected function setArray(array $arr);
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\Sso;

use LightSaml\State\Sso\SsoState;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class SsoStateSessionStore implements SsoStateStoreInterface
{
    /** @var SessionInterface */
    protected $session;

    /** @var string */
    protected $key;

    /**
     * @param SessionInterface $session
     * @param string           $key
     */
    public function __construct(SessionInterface $session, $key)
    {
        $this->session = $session;
        $this->key = $key;
    }

    /**
     * @return SsoState
     */
    public function get()
    {
        $result = $this->session->get($this->key);
        if (null == $result) {
            $result = new SsoState();
            $this->set($result);
        }

        return $result;
    }

    /**
     * @param SsoState $ssoState
     *
     * @return void
     */
    public function set(SsoState $ssoState)
    {
        $ssoState->setLocalSessionId($this->session->getId());
        $this->session->set($this->key, $ssoState);
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Action\Assertion\Inbound;

use LightSaml\Action\Assertion\AbstractAssertionAction;
use LightSaml\Context\Profile\AssertionContext;
use LightSaml\Provider\TimeProvider\TimeProviderInterface;
use LightSaml\Validator\Model\Assertion\AssertionTimeValidatorInterface;
use Psr\Log\LoggerInterface;

class TimeValidatorAction extends AbstractAssertionAction
{
    /** @var AssertionTimeValidatorInterface */
    protected $assertionTimeValidator;

    /** @var TimeProviderInterface */
    protected $timeProvider;

    /** @var int */
    protected $allowedSecondsSkew;

    /**
     * @param LoggerInterface                 $logger
     * @param AssertionTimeValidatorInterface $assertionTimeValidator
     * @param TimeProviderInterface           $timeProvider
     * @param int                             $allowedSecondsSkew
     */
    public function __construct(
        LoggerInterface $logger,
        AssertionTimeValidatorInterface $assertionTimeValidator,
        TimeProviderInterface $timeProvider,
        $allowedSecondsSkew = 120
    ) {
        parent::__construct($logger);

        $this->assertionTimeValidator = $assertionTimeValidator;
        $this->timeProvider = $timeProvider;
        $this->allowedSecondsSkew = $allowedSecondsSkew;
    }

    /**
     * @param AssertionContext $context
     *
     * @return void
     */
    protected function doExecute(AssertionContext $context)
    {
        $this->assertionTimeValidator->validateTimeRestrictions(
            $context->getAssertion(),
            $this->timeProvider->getTimestamp(),
            $this->allowedSecondsSkew
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Metadata;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\SamlConstants;

abstract class SSODescriptor extends RoleDescriptor
{
    /** @var SingleLogoutService[] */
    protected $singleLogoutServices = array();

    /** @var string[]|null */
    protected $nameIDFormats;

    /**
     * @param SingleLogoutService $singleLogoutService
     *
     * @return SSODescriptor
     */
    public function addSingleLogoutService(SingleLogoutService $singleLogoutService)
    {
        $this->singleLogoutServices[] = $singleLogoutService;

        return $this;
    }

    /**
     * @return SingleLogoutService[]
     */
    public function getAllSingleLogoutServices()
    {
        return $this->singleLogoutServices;
    }

    /**
     * @param string $binding
     *
     * @return SingleLogoutService[]
     */
    public function getAllSingleLogoutServicesByBinding($binding)
    {
        $result = array();
        foreach ($this->getAllSingleLogoutServices() as $svc) {
            if ($binding == $svc->getBinding()) {
                $result[] = $svc;
            }
        }

        return $result;
    }

    /**
     * @param string|null $binding
     *
     * @return SingleLogoutService|null
     */
    public function getFirstSingleLogoutService($binding = null)
    {
        foreach ($this->getAllSingleLogoutServices() as $svc) {
            if (null == $binding || $binding == $svc->getBinding()) {
                return $svc;
            }
        }

        return null;
    }

    /**
     * @param string $nameIDFormat
     *
     * @return SSODescriptor
     */
    public function addNameIDFormat($nameIDFormat)
    {
        $this->nameIDFormats[] = $nameIDFormat;

        return $this;
    }

    /**
     * @return null|string[]
     */
    public function getAllNameIDFormats()
    {
        return $this->nameIDFormats;
    }

    /**
     * @param string $nameIdFormat
     *
     * @return bool
     */
    public function hasNameIDFormat($nameIdFormat)
    {
        if ($this->nameIDFormats) {
            foreach ($this->nameIDFormats as $format) {
                if ($format == $nameIdFormat) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        parent::serialize($parent, $context);

        $this->manyElementsToXml($this->getAllSingleLogoutServices(), $parent, $context, null);
        $this->manyElementsToXml($this->getAllNameIDFormats(), $parent, $context, 'NameIDFormat', SamlConstants::NS_METADATA);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        parent::deserialize($node, $context);

        $this->manyElementsFromXml($node, $context, 'NameIDFormat', 'md', null, 'addNameIDFormat');

        $this->manyElementsFromXml(
            $node,
            $context,
            'SingleLogoutService',
            'md',
            'LightSaml\Model\Metadata\SingleLogoutService',
            'addSingleLogoutService'
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Credential;

use LightSaml\Credential\Context\CredentialContextSet;
use RobRichards\XMLSecLibs\XMLSecurityKey;

abstract class AbstractCredential implements CredentialInterface
{
    /** @var string */
    private $entityId;

    /** @var string */
    private $usageType;

    /** @var string[] */
    private $keyNames = array();

    /** @var XMLSecurityKey|null */
    private $publicKey;

    /** @var XMLSecurityKey|null */
    private $privateKey;

    /** @var string|null */
    private $secretKey;

    /** @var CredentialContextSet */
    private $credentialContext;

    public function __construct()
    {
        $this->credentialContext = new CredentialContextSet();
    }

    /**
     * @return string
     */
    public function getEntityId()
    {
        return $this->entityId;
    }

    /**
     * One of UsageType constants.
     *
     * @return string|null
     */
    public function getUsageType()
    {
        return $this->usageType;
    }

    /**
     * @return string[]
     */
    public function getKeyNames()
    {
        return $this->keyNames;
    }

    /**
     * @return XMLSecurityKey|null
     */
    public function getPublicKey()
    {
        return $this->publicKey;
    }

    /**
     * @return XMLSecurityKey|null
     */
    public function getPrivateKey()
    {
        return $this->privateKey;
    }

    /**
     * @return string|null
     */
    public function getSecretKey()
    {
        return $this->secretKey;
    }

    /**
     * @return CredentialContextSet
     */
    public function getCredentialContext()
    {
        return $this->credentialContext;
    }

    /**
     * @param CredentialContextSet $credentialContext
     *
     * @return AbstractCredential
     */
    public function setCredentialContext(CredentialContextSet $credentialContext)
    {
        $this->credentialContext = $credentialContext;

        return $this;
    }

    /**
     * @param string $entityId
     *
     * @return AbstractCredential
     */
    public function setEntityId($entityId)
    {
        $this->entityId = $entityId;

        return $this;
    }

    /**
     * @param \string[] $keyNames
     *
     * @return AbstractCredential
     */
    public function setKeyNames(array $keyNames)
    {
        $this->keyNames = $keyNames;

        return $this;
    }

    /**
     * @param string $keyName
     *
     * @return AbstractCredential
     */
    public function addKeyName($keyName)
    {
        $keyName = trim($keyName);
        if ($keyName) {
            $this->keyNames[] = $keyName;
        }

        return $this;
    }

    /**
     * @param null|XMLSecurityKey $privateKey
     *
     * @return AbstractCredential
     */
    public function setPrivateKey(XMLSecurityKey $privateKey)
    {
        $this->privateKey = $privateKey;

        return $this;
    }

    /**
     * @param null|XMLSecurityKey $publicKey
     *
     * @return AbstractCredential
     */
    public function setPublicKey(XMLSecurityKey $publicKey)
    {
        $this->publicKey = $publicKey;

        return $this;
    }

    /**
     * @param null|string $secretKey
     *
     * @return AbstractCredential
     */
    public function setSecretKey($secretKey)
    {
        $this->secretKey = $secretKey;

        return $this;
    }

    /**
     * @param string $usageType
     *
     * @return AbstractCredential
     */
    public function setUsageType($usageType)
    {
        $this->usageType = $usageType;

        return $this;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Resolver\Endpoint;

use LightSaml\Model\Metadata\EndpointReference;
use LightSaml\Model\Metadata\IndexedEndpoint;
use LightSaml\Resolver\Endpoint\Criteria\IndexCriteria;
use LightSaml\Criteria\CriteriaSet;

class IndexEndpointResolver implements EndpointResolverInterface
{
    /**
     * @param CriteriaSet         $criteriaSet
     * @param EndpointReference[] $candidates
     *
     * @return EndpointReference[]
     */
    public function resolve(CriteriaSet $criteriaSet, array $candidates)
    {
        if (false === $criteriaSet->has(IndexCriteria::class)) {
            return $candidates;
        }

        $result = array();
        /** @var IndexCriteria $indexCriteria */
        foreach ($criteriaSet->get(IndexCriteria::class) as $indexCriteria) {
            foreach ($candidates as $endpointReference) {
                $endpoint = $endpointReference->getEndpoint();
                if ($endpoint instanceof IndexedEndpoint) {
                    if ($endpoint->getIndex() == $indexCriteria->getIndex()) {
                        $result[] = $endpointReference;
                    }
                }
            }
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Context\Profile;

class ExceptionContext extends AbstractProfileContext
{
    /** @var \Exception */
    protected $exception;

    /** @var ExceptionContext|null */
    protected $nextExceptionContext;

    /**
     * @param \Exception|null $exception
     */
    public function __construct(\Exception $exception = null)
    {
        $this->exception = $exception;
    }

    /**
     * @return \Exception
     */
    public function getException()
    {
        return $this->exception;
    }

    /**
     * @return \Exception|null
     */
    public function getLastException()
    {
        if (null == $this->nextExceptionContext) {
            return $this->exception;
        }

        return $this->nextExceptionContext->getException();
    }

    /**
     * @return ExceptionContext|null
     */
    public function getNextExceptionContext()
    {
        return $this->nextExceptionContext;
    }

    /**
     * @param \Exception $exception
     *
     * @return ExceptionContext
     */
    public function addException(\Exception $exception)
    {
        if ($this->exception) {
            if (null == $this->nextExceptionContext) {
                $this->nextExceptionContext = new self($exception);

                return $this->nextExceptionContext;
            } else {
                return $this->nextExceptionContext->addException($exception);
            }
        } else {
            $this->exception = $exception;
        }

        return $this;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Validator\Model\NameId;

use LightSaml\Error\LightSamlValidationException;
use LightSaml\Helper;
use LightSaml\Model\Assertion\AbstractNameID;
use LightSaml\SamlConstants;

class NameIdValidator implements NameIdValidatorInterface
{
    private static $formatValidators = array(
        SamlConstants::NAME_ID_FORMAT_EMAIL => 'validateEmailFormat',
        SamlConstants::NAME_ID_FORMAT_X509_SUBJECT_NAME => 'validateX509SubjectNameFormat',
        SamlConstants::NAME_ID_FORMAT_WINDOWS => 'validateWindowsFormat',
        SamlConstants::NAME_ID_FORMAT_KERBEROS => 'validateKerberosFormat',
        SamlConstants::NAME_ID_FORMAT_ENTITY => 'validateEntityFormat',
        SamlConstants::NAME_ID_FORMAT_PERSISTENT => 'validatePersistentFormat',
        SamlConstants::NAME_ID_FORMAT_TRANSIENT => 'validateTransientFormat',
    );

    /**
     * @param AbstractNameID $nameId
     *
     * @throws \LightSaml\Error\LightSamlValidationException
     *
     * @return void
     */
    public function validateNameId(AbstractNameID $nameId)
    {
        if (false == $nameId->getFormat()) {
            return;
        }

        $this->validateFormat($nameId);

        $validatorMethod = isset(self::$formatValidators[$nameId->getFormat()]) ? self::$formatValidators[$nameId->getFormat()] : null;

        if ($validatorMethod) {
            $this->{$validatorMethod}($nameId);
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateWellFormedUriString($nameId->getFormat())) {
            throw new LightSamlValidationException(
                sprintf(
                    "NameID element has Format attribute '%s' which is not a wellformed absolute uri",
                    $nameId->getFormat()
                )
            );
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateEmailFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Email Format attribute MUST contain a Value that contains more than whitespace characters');
        }

        if (false == filter_var($nameId->getValue(), FILTER_VALIDATE_EMAIL)) {
            throw new LightSamlValidationException('Value of NameID is not a valid email address according to the IETF RFC 2822 specification');
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateX509SubjectNameFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with X509SubjectName Format attribute MUST contain a Value that contains more than whitespace characters');
        }

        // TODO: Consider checking for correct encoding of the Value according to the
        // XML Signature Recommendation (http://www.w3.org/TR/xmldsig-core/) section 4.4.4
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateWindowsFormat(AbstractNameID $nameId)
    {
        // Required format is 'DomainName\UserName' but the domain name and the '\' are optional
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Windows Format attribute MUST contain a Value that contains more than whitespace characters');
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateKerberosFormat(AbstractNameID $nameId)
    {
        // Required format is 'name[/instance]@REALM'
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Kerberos Format attribute MUST contain a Value that contains more than whitespace characters');
        }
        if (strlen($nameId->getValue()) < 3) {
            throw new LightSamlValidationException('NameID with Kerberos Format attribute MUST contain a Value with at least 3 characters');
        }
        if (false === strpos($nameId->getValue(), '@')) {
            throw new LightSamlValidationException("NameID with Kerberos Format attribute MUST contain a Value that contains a '@'");
        }
        // TODO: Consider implementing the rules for 'name', 'instance' and 'REALM' found in IETF RFC 1510 (http://www.ietf.org/rfc/rfc1510.txt) here
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateEntityFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Entity Format attribute MUST contain a Value that contains more than whitespace characters');
        }
        if (strlen($nameId->getValue()) > 1024) {
            throw new LightSamlValidationException('NameID with Entity Format attribute MUST have a Value that contains no more than 1024 characters');
        }
        if (false != $nameId->getNameQualifier()) {
            throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the NameQualifier attribute');
        }
        if (false != $nameId->getSPNameQualifier()) {
            throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the SPNameQualifier attribute');
        }
        if (false != $nameId->getSPProvidedID()) {
            throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the SPProvidedID attribute');
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validatePersistentFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Persistent Format attribute MUST contain a Value that contains more than whitespace characters');
        }
        if (strlen($nameId->getValue()) > 256) {
            throw new LightSamlValidationException('NameID with Persistent Format attribute MUST have a Value that contains no more than 256 characters');
        }
    }

    /**
     * @param AbstractNameID $nameId
     */
    protected function validateTransientFormat(AbstractNameID $nameId)
    {
        if (false == Helper::validateRequiredString($nameId->getValue())) {
            throw new LightSamlValidationException('NameID with Transient Format attribute MUST contain a Value that contains more than whitespace characters');
        }
        if (strlen($nameId->getValue()) > 256) {
            throw new LightSamlValidationException('NameID with Transient Format attribute MUST have a Value that contains no more than 256 characters');
        }
        if (false == Helper::validateIdString($nameId->getValue())) {
            throw new LightSamlValidationException(
                sprintf(
                    "NameID '%s' with Transient Format attribute MUST have a Value with at least 16 characters (the equivalent of 128 bits)",
                    $nameId->getValue()
                )
            );
        }
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Provider\Attribute;

use LightSaml\Context\Profile\AssertionContext;
use LightSaml\Model\Assertion\Attribute;

class FixedAttributeValueProvider implements AttributeValueProviderInterface
{
    /** @var Attribute[] */
    protected $attributes = array();

    /**
     * @param Attribute $attribute
     *
     * @return FixedAttributeValueProvider
     */
    public function add(Attribute $attribute)
    {
        $this->attributes[] = $attribute;

        return $this;
    }

    /**
     * @param \LightSaml\Model\Assertion\Attribute[] $attributes
     *
     * @return FixedAttributeValueProvider
     */
    public function setAttributes(array $attributes)
    {
        $this->attributes = [];
        foreach ($attributes as $attribute) {
            $this->add($attribute);
        }

        return $this;
    }

    /**
     * @param AssertionContext $context
     *
     * @return Attribute[]
     */
    public function getValues(AssertionContext $context)
    {
        return $this->attributes;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Bridge\Pimple\Container\Factory;

use LightSaml\Bridge\Pimple\Container\CredentialContainer;
use LightSaml\Build\Container\OwnContainerInterface;
use LightSaml\Build\Container\PartyContainerInterface;
use LightSaml\Credential\CredentialInterface;
use LightSaml\Error\LightSamlBuildException;
use LightSaml\Store\Credential\Factory\CredentialFactory;
use Pimple\Container;
use Pimple\ServiceProviderInterface;

class CredentialContainerProvider implements ServiceProviderInterface
{
    /** @var PartyContainerInterface */
    private $partyContainer;

    /** @var OwnContainerInterface */
    private $ownContainer;

    /** @var CredentialInterface[] */
    private $extraCredentials = array();

    /**
     * @param PartyContainerInterface $partyContainer
     * @param OwnContainerInterface   $ownContainer
     */
    public function __construct(PartyContainerInterface $partyContainer, OwnContainerInterface $ownContainer)
    {
        $this->ownContainer = $ownContainer;
        $this->partyContainer = $partyContainer;
    }

    /**
     * @param CredentialInterface $credential
     *
     * @return CredentialContainerProvider
     */
    public function addExtraCredential(CredentialInterface $credential)
    {
        if (null === $credential->getEntityId()) {
            throw new \InvalidArgumentException('Extra credential must have entityID');
        }

        $this->extraCredentials[] = $credential;

        return $this;
    }

    /**
     * @param Container $pimple A container instance
     */
    public function register(Container $pimple)
    {
        $ownCredentials = $this->ownContainer->getOwnCredentials();
        if (empty($ownCredentials)) {
            throw new LightSamlBuildException('There are no own credentials');
        }

        $pimple[CredentialContainer::CREDENTIAL_STORE] = function () {
            $factory = new CredentialFactory();

            return $factory->build(
                $this->partyContainer->getIdpEntityDescriptorStore(),
                $this->partyContainer->getSpEntityDescriptorStore(),
                $this->ownContainer->getOwnCredentials(),
                $this->extraCredentials
            );
        };
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\Id;

class IdArrayStore implements IdStoreInterface
{
    /** @var array */
    protected $store = array();

    /**
     * @param string    $entityId
     * @param string    $id
     * @param \DateTime $expiryTime
     *
     * @return void
     */
    public function set($entityId, $id, \DateTime $expiryTime)
    {
        if (false == isset($this->store[$entityId])) {
            $this->store[$entityId] = array();
        }
        $this->store[$entityId][$id] = $expiryTime;
    }

    /**
     * @param string $entityId
     * @param string $id
     *
     * @return bool
     */
    public function has($entityId, $id)
    {
        return isset($this->store[$entityId][$id]);
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Resolver\Endpoint;

use LightSaml\Model\Metadata\EndpointReference;
use LightSaml\Criteria\CriteriaSet;
use LightSaml\Resolver\Endpoint\Criteria\BindingCriteria;

class BindingEndpointResolver implements EndpointResolverInterface
{
    /**
     * @param CriteriaSet         $criteriaSet
     * @param EndpointReference[] $candidates
     *
     * @return EndpointReference[]
     */
    public function resolve(CriteriaSet $criteriaSet, array $candidates)
    {
        if (false === $criteriaSet->has(BindingCriteria::class)) {
            return $candidates;
        }

        $arrOrdered = array();
        /** @var BindingCriteria $bindingCriteria */
        foreach ($criteriaSet->get(BindingCriteria::class) as $bindingCriteria) {
            foreach ($candidates as $endpointReference) {
                $preference = $bindingCriteria->getPreference($endpointReference->getEndpoint()->getBinding());
                if (null !== $preference) {
                    $arrOrdered[$preference][] = $endpointReference;
                }
            }
        }

        ksort($arrOrdered);

        $result = array();
        foreach ($arrOrdered as $arr) {
            foreach ($arr as $endpointReference) {
                $result[] = $endpointReference;
            }
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Resolver\Signature;

use LightSaml\Context\Profile\AbstractProfileContext;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Error\LightSamlContextException;
use LightSaml\Model\XmlDSig\SignatureWriter;
use LightSaml\Resolver\Credential\CredentialResolverInterface;
use LightSaml\SamlConstants;
use LightSaml\Credential\UsageType;
use LightSaml\Credential\X509CredentialInterface;
use LightSaml\Credential\Criteria\EntityIdCriteria;
use LightSaml\Credential\Criteria\MetadataCriteria;
use LightSaml\Credential\Criteria\UsageCriteria;
use LightSaml\Credential\Criteria\X509CredentialCriteria;

class OwnSignatureResolver implements SignatureResolverInterface
{
    /** @var CredentialResolverInterface */
    protected $credentialResolver;

    /**
     * @param CredentialResolverInterface $credentialResolver
     */
    public function __construct(CredentialResolverInterface $credentialResolver)
    {
        $this->credentialResolver = $credentialResolver;
    }

    /**
     * @param AbstractProfileContext $context
     *
     * @return SignatureWriter
     */
    public function getSignature(AbstractProfileContext $context)
    {
        $credential = $this->getSigningCredential($context);
        if (null == $credential) {
            throw new LightSamlContextException($context, 'Unable to find signing credential');
        }
        $trustOptions = $context->getProfileContext()->getTrustOptions();

        $signature = new SignatureWriter($credential->getCertificate(), $credential->getPrivateKey(), $trustOptions->getSignatureDigestAlgorithm());

        return $signature;
    }

    /**
     * @param AbstractProfileContext $context
     *
     * @return X509CredentialInterface|null
     */
    private function getSigningCredential(AbstractProfileContext $context)
    {
        $profileContext = $context->getProfileContext();

        $entityDescriptor = $profileContext->getOwnEntityDescriptor();

        $query = $this->credentialResolver->query();
        $query
            ->add(new EntityIdCriteria($entityDescriptor->getEntityID()))
            ->add(new UsageCriteria(UsageType::SIGNING))
            ->add(new X509CredentialCriteria())
            ->addIf(ProfileContext::ROLE_IDP === $profileContext->getOwnRole(), function () {
                return new MetadataCriteria(MetadataCriteria::TYPE_IDP, SamlConstants::VERSION_20);
            })
            ->addIf(ProfileContext::ROLE_SP === $profileContext->getOwnRole(), function () {
                return new MetadataCriteria(MetadataCriteria::TYPE_SP, SamlConstants::VERSION_20);
            })
        ;
        $query->resolve();

        $result = $query->firstCredential();
        if ($result && false === $result instanceof X509CredentialInterface) {
            throw new \LogicException(sprintf('Expected X509CredentialInterface but got %s', get_class($result)));
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Context\Profile;

use LightSaml\State\Sso\SsoSessionState;

class LogoutContext extends AbstractProfileContext
{
    /** @var SsoSessionState|null */
    protected $ssoSessionState;

    /** @var bool */
    protected $allSsoSessionsTerminated = false;

    /**
     * @return SsoSessionState|null
     */
    public function getSsoSessionState()
    {
        return $this->ssoSessionState;
    }

    /**
     * @param SsoSessionState $ssoSessionState
     *
     * @return LogoutContext
     */
    public function setSsoSessionState(SsoSessionState $ssoSessionState)
    {
        $this->ssoSessionState = $ssoSessionState;
        $this->allSsoSessionsTerminated = false;

        return $this;
    }

    /**
     * @return bool
     */
    public function areAllSsoSessionsTerminated()
    {
        return $this->allSsoSessionsTerminated;
    }

    /**
     * @param bool $allSsoSessionsTerminated
     *
     * @return LogoutContext
     */
    public function setAllSsoSessionsTerminated($allSsoSessionsTerminated)
    {
        $this->allSsoSessionsTerminated = (bool) $allSsoSessionsTerminated;

        return $this;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\AbstractSamlModel;
use LightSaml\SamlConstants;

class Attribute extends AbstractSamlModel
{
    /** @var string */
    protected $name;

    /** @var string */
    protected $nameFormat;

    /** @var string */
    protected $friendlyName;

    /** @var string[] */
    protected $attributeValue;

    /**
     * @param string|null     $name
     * @param string|string[] $value
     */
    public function __construct($name = null, $value = null)
    {
        $this->name = $name;
        if ($value) {
            $this->attributeValue = is_array($value) ? $value : array($value);
        }
    }

    /**
     * @param string $attributeValue
     *
     * @return Attribute
     */
    public function addAttributeValue($attributeValue)
    {
        if (false == is_array($this->attributeValue)) {
            $this->attributeValue = array();
        }
        $this->attributeValue[] = $attributeValue;

        return $this;
    }

    /**
     * @param string[]|string $attributeValue
     *
     * @return Attribute
     */
    public function setAttributeValue($attributeValue)
    {
        if (false == is_array($attributeValue)) {
            $attributeValue = array($attributeValue);
        }
        $this->attributeValue = $attributeValue;

        return $this;
    }

    /**
     * @return \string[]
     */
    public function getAllAttributeValues()
    {
        return $this->attributeValue;
    }

    /**
     * @return string|null
     */
    public function getFirstAttributeValue()
    {
        $arr = $this->attributeValue;

        return array_shift($arr);
    }

    /**
     * @param string $friendlyName
     *
     * @return Attribute
     */
    public function setFriendlyName($friendlyName)
    {
        $this->friendlyName = $friendlyName;

        return $this;
    }

    /**
     * @return string
     */
    public function getFriendlyName()
    {
        return $this->friendlyName;
    }

    /**
     * @param string $name
     *
     * @return Attribute
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $nameFormat
     *
     * @return Attribute
     */
    public function setNameFormat($nameFormat)
    {
        $this->nameFormat = $nameFormat;

        return $this;
    }

    /**
     * @return string
     */
    public function getNameFormat()
    {
        return $this->nameFormat;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     *
     * @return void
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('Attribute', SamlConstants::NS_ASSERTION, $parent, $context);

        $this->attributesToXml(array('Name', 'NameFormat', 'FriendlyName'), $result);

        $this->manyElementsToXml($this->getAllAttributeValues(), $result, $context, 'AttributeValue', SamlConstants::NS_ASSERTION);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'Attribute', SamlConstants::NS_ASSERTION);

        $this->attributesFromXml($node, array('Name', 'NameFormat', 'FriendlyName'));

        $this->attributeValue = array();
        $this->manyElementsFromXml($node, $context, 'AttributeValue', 'saml', null, 'addAttributeValue');
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Helper;
use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\AbstractSamlModel;
use LightSaml\Model\XmlDSig\Signature;
use LightSaml\SamlConstants;

class Assertion extends AbstractSamlModel
{
    //region Attributes

    /**
     * @var string
     */
    protected $id;

    /**
     * @var string
     */
    protected $version = SamlConstants::VERSION_20;

    /**
     * @var int
     */
    protected $issueInstant;

    //endregion

    //region Elements

    /**
     * @var Issuer
     */
    protected $issuer;

    /**
     * @var Signature|null
     */
    protected $signature;

    /**
     * @var Subject|null
     */
    protected $subject;

    /**
     * @var Conditions|null
     */
    protected $conditions;

    /**
     * @var array|AbstractStatement[]|AuthnStatement[]|AttributeStatement[]
     */
    protected $items = array();

    //endregion

    /**
     * Core 3.3.4 Processing rules.
     *
     * @param string      $nameId
     * @param string|null $format
     *
     * @return bool
     */
    public function equals($nameId, $format)
    {
        if (false == $this->getSubject()) {
            return false;
        }

        if (false == $this->getSubject()->getNameID()) {
            return false;
        }

        if ($this->getSubject()->getNameID()->getValue() != $nameId) {
            return false;
        }

        if ($this->getSubject()->getNameID()->getFormat() != $format) {
            return false;
        }

        return true;
    }

    /**
     * @param string $sessionIndex
     *
     * @return bool
     */
    public function hasSessionIndex($sessionIndex)
    {
        if (null == $this->getAllAuthnStatements()) {
            return false;
        }

        foreach ($this->getAllAuthnStatements() as $authnStatement) {
            if ($authnStatement->getSessionIndex() == $sessionIndex) {
                return true;
            }
        }

        return false;
    }

    public function hasAnySessionIndex()
    {
        if (false == $this->getAllAuthnStatements()) {
            return false;
        }

        foreach ($this->getAllAuthnStatements() as $authnStatement) {
            if ($authnStatement->getSessionIndex()) {
                return true;
            }
        }

        return false;
    }

    //region Getters & Setters

    /**
     * @param Conditions|null $conditions
     *
     * @return Assertion
     */
    public function setConditions(Conditions $conditions = null)
    {
        $this->conditions = $conditions;

        return $this;
    }

    /**
     * @return \LightSaml\Model\Assertion\Conditions|null
     */
    public function getConditions()
    {
        return $this->conditions;
    }

    /**
     * @param string $id
     *
     * @return Assertion
     */
    public function setId($id)
    {
        $this->id = (string) $id;

        return $this;
    }

    /**
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param string|int|\DateTime $issueInstant
     *
     * @throws \InvalidArgumentException
     *
     * @return Assertion
     */
    public function setIssueInstant($issueInstant)
    {
        $this->issueInstant = Helper::getTimestampFromValue($issueInstant);

        return $this;
    }

    /**
     * @return int
     */
    public function getIssueInstantTimestamp()
    {
        return $this->issueInstant;
    }

    /**
     * @return string
     */
    public function getIssueInstantString()
    {
        if ($this->issueInstant) {
            return Helper::time2string($this->issueInstant);
        }

        return null;
    }

    /**
     * @return string
     */
    public function getIssueInstantDateTime()
    {
        if ($this->issueInstant) {
            return new \DateTime('@'.$this->issueInstant);
        }

        return null;
    }

    /**
     * @param Issuer $issuer
     *
     * @return Assertion
     */
    public function setIssuer(Issuer $issuer = null)
    {
        $this->issuer = $issuer;

        return $this;
    }

    /**
     * @return \LightSaml\Model\Assertion\Issuer
     */
    public function getIssuer()
    {
        return $this->issuer;
    }

    /**
     * @param Signature $signature
     *
     * @return Assertion
     */
    public function setSignature(Signature $signature = null)
    {
        $this->signature = $signature;

        return $this;
    }

    /**
     * @return \LightSaml\Model\XmlDSig\Signature|null
     */
    public function getSignature()
    {
        return $this->signature;
    }

    /**
     * @param Subject $subject
     *
     * @return Assertion
     */
    public function setSubject(Subject $subject)
    {
        $this->subject = $subject;

        return $this;
    }

    /**
     * @return \LightSaml\Model\Assertion\Subject
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * @param string $version
     *
     * @return Assertion
     */
    public function setVersion($version)
    {
        $this->version = (string) $version;

        return $this;
    }

    /**
     * @return string
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * @param AbstractStatement $statement
     *
     * @return Assertion
     */
    public function addItem(AbstractStatement $statement)
    {
        $this->items[] = $statement;

        return $this;
    }

    /**
     * @return AbstractStatement[]|AttributeStatement[]|AuthnStatement[]|array
     */
    public function getAllItems()
    {
        return $this->items;
    }

    /**
     * @return \LightSaml\Model\Assertion\AuthnStatement[]
     */
    public function getAllAuthnStatements()
    {
        $result = array();
        foreach ($this->items as $item) {
            if ($item instanceof AuthnStatement) {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * @return \LightSaml\Model\Assertion\AttributeStatement[]
     */
    public function getAllAttributeStatements()
    {
        $result = array();
        foreach ($this->items as $item) {
            if ($item instanceof AttributeStatement) {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * @return \LightSaml\Model\Assertion\AttributeStatement|null
     */
    public function getFirstAttributeStatement()
    {
        foreach ($this->items as $item) {
            if ($item instanceof AttributeStatement) {
                return $item;
            }
        }

        return null;
    }

    /**
     * @return \LightSaml\Model\Assertion\AuthnStatement|null
     */
    public function getFirstAuthnStatement()
    {
        foreach ($this->items as $item) {
            if ($item instanceof AuthnStatement) {
                return $item;
            }
        }

        return null;
    }

    //endregion

    /**
     * @return bool
     */
    public function hasBearerSubject()
    {
        if ($this->getAllAuthnStatements() && $this->getSubject()) {
            if ($this->getSubject()->getBearerConfirmations()) {
                return true;
            }
        }

        return false;
    }

    protected function prepareForXml()
    {
        if (false == $this->getId()) {
            $this->setId(Helper::generateID());
        }
        if (false == $this->getIssueInstantTimestamp()) {
            $this->setIssueInstant(time());
        }
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     *
     * @return void
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $this->prepareForXml();

        $result = $this->createElement('Assertion', SamlConstants::NS_ASSERTION, $parent, $context);

        $this->attributesToXml(array('ID', 'Version', 'IssueInstant'), $result);

        $this->singleElementsToXml(
            array('Issuer', 'Subject', 'Conditions'),
            $result,
            $context
        );

        foreach ($this->items as $item) {
            $item->serialize($result, $context);
        }

        // must be added at the end
        $this->singleElementsToXml(array('Signature'), $result, $context);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'Assertion', SamlConstants::NS_ASSERTION);

        $this->attributesFromXml($node, array('ID', 'Version', 'IssueInstant'));

        $this->singleElementsFromXml($node, $context, array(
            'Issuer' => array('saml', 'LightSaml\Model\Assertion\Issuer'),
            'Subject' => array('saml', 'LightSaml\Model\Assertion\Subject'),
            'Conditions' => array('saml', 'LightSaml\Model\Assertion\Conditions'),
        ));

        $this->manyElementsFromXml(
            $node,
            $context,
            'AuthnStatement',
            'saml',
            'LightSaml\Model\Assertion\AuthnStatement',
            'addItem'
        );

        $this->manyElementsFromXml(
            $node,
            $context,
            'AttributeStatement',
            'saml',
            'LightSaml\Model\Assertion\AttributeStatement',
            'addItem'
        );

        $this->singleElementsFromXml($node, $context, array(
            'Signature' => array('ds', 'LightSaml\Model\XmlDSig\SignatureXmlReader'),
        ));
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\AbstractSamlModel;
use LightSaml\SamlConstants;

class Subject extends AbstractSamlModel
{
    /** @var NameID */
    protected $nameId;

    /** @var SubjectConfirmation[] */
    protected $subjectConfirmation = array();

    /**
     * @param NameID $nameId
     *
     * @return Subject
     */
    public function setNameID(NameID $nameId = null)
    {
        $this->nameId = $nameId;

        return $this;
    }

    /**
     * @return \LightSaml\Model\Assertion\NameID
     */
    public function getNameID()
    {
        return $this->nameId;
    }

    /**
     * @param SubjectConfirmation $subjectConfirmation
     *
     * @return Subject
     */
    public function addSubjectConfirmation(SubjectConfirmation $subjectConfirmation)
    {
        $this->subjectConfirmation[] = $subjectConfirmation;

        return $this;
    }

    /**
     * @return SubjectConfirmation[]
     */
    public function getAllSubjectConfirmations()
    {
        return $this->subjectConfirmation;
    }

    /**
     * @return SubjectConfirmation|null
     */
    public function getFirstSubjectConfirmation()
    {
        if (is_array($this->subjectConfirmation) && isset($this->subjectConfirmation[0])) {
            return $this->subjectConfirmation[0];
        }

        return;
    }

    /**
     * Returns array of <SubjectConfirmation> containing a Method of urn:oasis:names:tc:SAML:2.0:cm:bearer.
     *
     * @return SubjectConfirmation[]
     */
    public function getBearerConfirmations()
    {
        $result = array();
        if ($this->getAllSubjectConfirmations()) {
            foreach ($this->getAllSubjectConfirmations() as $confirmation) {
                if (SamlConstants::CONFIRMATION_METHOD_BEARER == $confirmation->getMethod()) {
                    $result[] = $confirmation;
                    break;
                }
            }
        }

        return $result;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     *
     * @return void
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('Subject', SamlConstants::NS_ASSERTION, $parent, $context);

        $this->singleElementsToXml(array('NameID'), $result, $context);
        $this->manyElementsToXml($this->getAllSubjectConfirmations(), $result, $context, null);
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'Subject', SamlConstants::NS_ASSERTION);

        $this->singleElementsFromXml($node, $context, array(
            'NameID' => array('saml', 'LightSaml\Model\Assertion\NameID'),
        ));

        $this->manyElementsFromXml(
            $node,
            $context,
            'SubjectConfirmation',
            'saml',
            'LightSaml\Model\Assertion\SubjectConfirmation',
            'addSubjectConfirmation'
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Action\Profile\Inbound\Message;

use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Binding\BindingFactoryInterface;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Error\LightSamlBindingException;
use Psr\Log\LoggerInterface;

/**
 * Receives message from HTTP Request into inbound context,
 * optionally enforces biding type to the one specified in the inbound context.
 */
class ReceiveMessageAction extends AbstractProfileAction
{
    /** @var BindingFactoryInterface */
    protected $bindingFactory;

    /**
     * @param LoggerInterface         $logger
     * @param BindingFactoryInterface $bindingFactory
     */
    public function __construct(LoggerInterface $logger, BindingFactoryInterface $bindingFactory)
    {
        parent::__construct($logger);

        $this->bindingFactory = $bindingFactory;
    }

    /**
     * @param ProfileContext $context
     *
     * @return void
     */
    protected function doExecute(ProfileContext $context)
    {
        $bindingType = $this->bindingFactory->detectBindingType($context->getHttpRequest());
        if (null == $bindingType) {
            $message = 'Unable to resolve binding type, invalid or unsupported http request';
            $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this));
            throw new LightSamlBindingException($message);
        }

        $this->logger->debug(sprintf('Detected binding type: %s', $bindingType), LogHelper::getActionContext($context, $this));

        $binding = $this->bindingFactory->create($bindingType);
        $binding->receive($context->getHttpRequest(), $context->getInboundContext());
        $context->getInboundContext()->setBindingType($bindingType);

        $this->logger->info(
            'Received message',
            LogHelper::getActionContext($context, $this, array(
                'message' => $context->getInboundContext()->getDeserializationContext()->getDocument()->saveXML(),
            ))
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\EntityDescriptor;

use LightSaml\Model\Metadata\EntitiesDescriptor;
use LightSaml\Model\Metadata\EntityDescriptor;

class FixedEntityDescriptorStore implements EntityDescriptorStoreInterface
{
    /** @var array|EntityDescriptor[] entityId=>descriptor */
    protected $descriptors = array();

    /**
     * @param EntityDescriptor|EntitiesDescriptor $entityDescriptor
     *
     * @return FixedEntityDescriptorStore
     *
     * @throws \InvalidArgumentException
     */
    public function add($entityDescriptor)
    {
        if ($entityDescriptor instanceof EntityDescriptor) {
            if (false == $entityDescriptor->getEntityID()) {
                throw new \InvalidArgumentException('EntityDescriptor must have entityId set');
            }
            $this->descriptors[$entityDescriptor->getEntityID()] = $entityDescriptor;
        } elseif ($entityDescriptor instanceof EntitiesDescriptor) {
            foreach ($entityDescriptor->getAllItems() as $item) {
                $this->add($item);
            }
        } else {
            throw new \InvalidArgumentException('Expected EntityDescriptor or EntitiesDescriptor');
        }

        return $this;
    }

    /**
     * @param string $entityId
     *
     * @return EntityDescriptor|null
     */
    public function get($entityId)
    {
        if (isset($this->descriptors[$entityId])) {
            return $this->descriptors[$entityId];
        }

        return null;
    }

    /**
     * @param string $entityId
     *
     * @return bool
     */
    public function has($entityId)
    {
        return isset($this->descriptors[$entityId]);
    }

    /**
     * @return array|EntityDescriptor[]
     */
    public function all()
    {
        return array_values($this->descriptors);
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Action\Profile\Outbound\Message;

use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\Helper\MessageContextHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Provider\TimeProvider\TimeProviderInterface;
use Psr\Log\LoggerInterface;

/**
 * Sets outbound message IssueInstant to the value provided by given time provider.
 */
class MessageIssueInstantAction extends AbstractProfileAction
{
    /** @var TimeProviderInterface */
    protected $timeProvider;

    /**
     * @param LoggerInterface       $logger
     * @param TimeProviderInterface $timeProvider
     */
    public function __construct(LoggerInterface $logger, TimeProviderInterface $timeProvider)
    {
        parent::__construct($logger);

        $this->timeProvider = $timeProvider;
    }

    /**
     * @param ProfileContext $context
     *
     * @return void
     */
    protected function doExecute(ProfileContext $context)
    {
        MessageContextHelper::asSamlMessage($context->getOutboundContext())
            ->setIssueInstant($this->timeProvider->getTimestamp());

        $this->logger->info(
            sprintf('Message IssueInstant set to "%s"', MessageContextHelper::asSamlMessage($context->getOutboundContext())->getIssueInstantString()),
            LogHelper::getActionContext($context, $this)
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Resolver\Credential;

use LightSaml\Credential\CredentialInterface;
use LightSaml\Criteria\CriteriaSet;
use LightSaml\Credential\Criteria\UsageCriteria;

class UsageFilterResolver extends AbstractQueryableResolver
{
    /**
     * @param CriteriaSet           $criteriaSet
     * @param CredentialInterface[] $arrCredentials
     *
     * @return CredentialInterface[]
     */
    public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array())
    {
        if (false == $criteriaSet->has(UsageCriteria::class)) {
            return $arrCredentials;
        }

        $result = array();
        foreach ($criteriaSet->get(UsageCriteria::class) as $criteria) {
            /* @var UsageCriteria $criteria */
            foreach ($arrCredentials as $credential) {
                if (false == $credential->getUsageType() || $criteria->getUsage() == $credential->getUsageType()) {
                    $result[] = $credential;
                }
            }
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Model\Context\SerializationContext;
use LightSaml\SamlConstants;

class EncryptedAssertionWriter extends EncryptedElementWriter
{
    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     *
     * @return \DOMElement
     */
    protected function createRootElement(\DOMNode $parent, SerializationContext $context)
    {
        return $this->createElement('saml:EncryptedAssertion', SamlConstants::NS_ASSERTION, $parent, $context);
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\Credential;

use LightSaml\Credential\CredentialInterface;
use LightSaml\Credential\X509Credential;
use LightSaml\Credential\KeyHelper;
use LightSaml\Credential\X509Certificate;

class X509FileCredentialStore implements CredentialStoreInterface
{
    /** @var string */
    private $entityId;

    /** @var string */
    private $certificatePath;

    /** @var string */
    private $keyPath;

    /** @var string */
    private $password;

    /** @var X509Credential */
    private $credential;

    /**
     * @param string $entityId
     * @param string $certificatePath
     * @param string $keyPath
     * @param string $password
     */
    public function __construct($entityId, $certificatePath, $keyPath, $password)
    {
        $this->entityId = $entityId;
        $this->certificatePath = $certificatePath;
        $this->keyPath = $keyPath;
        $this->password = $password;
    }

    /**
     * @param string $entityId
     *
     * @return CredentialInterface[]
     */
    public function getByEntityId($entityId)
    {
        if ($entityId != $this->entityId) {
            return [];
        }

        if (null == $this->credential) {
            $certificate = X509Certificate::fromFile($this->certificatePath);
            $this->credential = new X509Credential(
                $certificate,
                KeyHelper::createPrivateKey($this->keyPath, $this->password, true, $certificate->getSignatureAlgorithm())
            );
            $this->credential->setEntityId($this->entityId);
        }

        return [$this->credential];
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Resolver\Credential;

use LightSaml\Credential\CredentialInterface;
use LightSaml\Credential\Criteria\AlgorithmCriteria;
use LightSaml\Criteria\CriteriaSet;

class AlgorithmFilterResolver extends AbstractQueryableResolver
{
    /**
     * @param CriteriaSet           $criteriaSet
     * @param CredentialInterface[] $arrCredentials
     *
     * @return CredentialInterface[]
     */
    public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array())
    {
        if (false == $criteriaSet->has(AlgorithmCriteria::class)) {
            return $arrCredentials;
        }

        $result = array();
        foreach ($criteriaSet->get(AlgorithmCriteria::class) as $criteria) {
            /* @var AlgorithmCriteria $criteria */
            foreach ($arrCredentials as $credential) {
                if (($credential->getPrivateKey() && $credential->getPrivateKey()->getAlgorith() == $criteria->getAlgorithm()) ||
                    ($credential->getPublicKey() && $credential->getPublicKey()->getAlgorith() == $criteria->getAlgorithm())
                ) {
                    $result[] = $credential;
                }
            }
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Assertion;

use LightSaml\Helper;
use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\AbstractSamlModel;
use LightSaml\SamlConstants;

class Conditions extends AbstractSamlModel
{
    /**
     * @var int|null
     */
    protected $notBefore;

    /**
     * @var int|null
     */
    protected $notOnOrAfter;

    /**
     * @var array|AbstractCondition[]|AudienceRestriction[]|OneTimeUse[]|ProxyRestriction[]
     */
    protected $items = array();

    /**
     * @param AbstractCondition $item
     *
     * @return Conditions
     */
    public function addItem(AbstractCondition $item)
    {
        $this->items[] = $item;

        return $this;
    }

    /**
     * @return AbstractCondition[]|AudienceRestriction[]|OneTimeUse[]|ProxyRestriction[]|array
     */
    public function getAllItems()
    {
        return $this->items;
    }

    /**
     * @return \LightSaml\Model\Assertion\AudienceRestriction[]
     */
    public function getAllAudienceRestrictions()
    {
        $result = array();
        foreach ($this->items as $item) {
            if ($item instanceof AudienceRestriction) {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * @return \LightSaml\Model\Assertion\AudienceRestriction|null
     */
    public function getFirstAudienceRestriction()
    {
        foreach ($this->items as $item) {
            if ($item instanceof AudienceRestriction) {
                return $item;
            }
        }

        return null;
    }

    /**
     * @return \LightSaml\Model\Assertion\OneTimeUse[]
     */
    public function getAllOneTimeUses()
    {
        $result = array();
        foreach ($this->items as $item) {
            if ($item instanceof OneTimeUse) {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * @return \LightSaml\Model\Assertion\OneTimeUse|null
     */
    public function getFirstOneTimeUse()
    {
        foreach ($this->items as $item) {
            if ($item instanceof OneTimeUse) {
                return $item;
            }
        }

        return null;
    }

    /**
     * @return \LightSaml\Model\Assertion\ProxyRestriction[]
     */
    public function getAllProxyRestrictions()
    {
        $result = array();
        foreach ($this->items as $item) {
            if ($item instanceof ProxyRestriction) {
                $result[] = $item;
            }
        }

        return $result;
    }

    /**
     * @return \LightSaml\Model\Assertion\ProxyRestriction|null
     */
    public function getFirstProxyRestriction()
    {
        foreach ($this->items as $item) {
            if ($item instanceof ProxyRestriction) {
                return $item;
            }
        }

        return null;
    }

    /**
     * @param int|string|\DateTime|null $notBefore
     *
     * @return Conditions
     */
    public function setNotBefore($notBefore)
    {
        $this->notBefore = Helper::getTimestampFromValue($notBefore);

        return $this;
    }

    /**
     * @return int|null
     */
    public function getNotBeforeTimestamp()
    {
        return $this->notBefore;
    }

    /**
     * @return int|null
     */
    public function getNotBeforeString()
    {
        if ($this->notBefore) {
            return Helper::time2string($this->notBefore);
        }

        return null;
    }

    /**
     * @return \DateTime|null
     */
    public function getNotBeforeDateTime()
    {
        if ($this->notBefore) {
            return new \DateTime('@'.$this->notBefore);
        }

        return null;
    }

    /**
     * @param int|null $notOnOrAfter
     *
     * @return Conditions
     */
    public function setNotOnOrAfter($notOnOrAfter)
    {
        $this->notOnOrAfter = Helper::getTimestampFromValue($notOnOrAfter);

        return $this;
    }

    /**
     * @return int|null
     */
    public function getNotOnOrAfterTimestamp()
    {
        return $this->notOnOrAfter;
    }

    /**
     * @return string|null
     */
    public function getNotOnOrAfterString()
    {
        if ($this->notOnOrAfter) {
            return Helper::time2string($this->notOnOrAfter);
        }

        return null;
    }

    /**
     * @return \DateTime|null
     */
    public function getNotOnOrAfterDateTime()
    {
        if ($this->notOnOrAfter) {
            return new \DateTime('@'.$this->notOnOrAfter);
        }

        return null;
    }

    /**
     * @param \DOMNode             $parent
     * @param SerializationContext $context
     *
     * @return void
     */
    public function serialize(\DOMNode $parent, SerializationContext $context)
    {
        $result = $this->createElement('Conditions', SamlConstants::NS_ASSERTION, $parent, $context);

        $this->attributesToXml(
            array('NotBefore', 'NotOnOrAfter'),
            $result
        );

        foreach ($this->items as $item) {
            $item->serialize($result, $context);
        }
    }

    /**
     * @param \DOMNode               $node
     * @param DeserializationContext $context
     */
    public function deserialize(\DOMNode $node, DeserializationContext $context)
    {
        $this->checkXmlNodeName($node, 'Conditions', SamlConstants::NS_ASSERTION);

        $this->attributesFromXml($node, array('NotBefore', 'NotOnOrAfter'));

        $this->manyElementsFromXml(
            $node,
            $context,
            'AudienceRestriction',
            'saml',
            'LightSaml\Model\Assertion\AudienceRestriction',
            'addItem'
        );
        $this->manyElementsFromXml(
            $node,
            $context,
            'OneTimeUse',
            'saml',
            'LightSaml\Model\Assertion\OneTimeUse',
            'addItem'
        );
        $this->manyElementsFromXml(
            $node,
            $context,
            'ProxyRestriction',
            'saml',
            'LightSaml\Model\Assertion\ProxyRestriction',
            'addItem'
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Model\Metadata;

use LightSaml\Error\LightSamlXmlException;
use LightSaml\Model\AbstractSamlModel;
use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\SamlElementInterface;
use LightSaml\SamlConstants;

abstract class Metadata extends AbstractSamlModel
{
    /**
     * @param string $path
     *
     * @return EntitiesDescriptor|EntityDescriptor
     */
    public static function fromFile($path)
    {
        $deserializatonContext = new DeserializationContext();
        $xml = file_get_contents($path);

        return self::fromXML($xml, $deserializatonContext);
    }

    /**
     * @param string                 $xml
     * @param DeserializationContext $context
     *
     * @return EntityDescriptor|EntitiesDescriptor
     *
     * @throws \Exception
     */
    public static function fromXML($xml, DeserializationContext $context)
    {
        if (false == is_string($xml)) {
            throw new \InvalidArgumentException('Expecting string');
        }

        $context->getDocument()->loadXML($xml);

        $node = $context->getDocument()->firstChild;
        while ($node && $node instanceof \DOMComment) {
            $node = $node->nextSibling;
        }
        if (null === $node) {
            throw new LightSamlXmlException('Empty XML');
        }

        if (SamlConstants::NS_METADATA !== $node->namespaceURI) {
            throw new LightSamlXmlException(sprintf(
                "Invalid namespace '%s' of the root XML element, expected '%s'",
                $node->namespaceURI,
                SamlConstants::NS_METADATA
            ));
        }

        $map = array(
            'EntityDescriptor' => '\LightSaml\Model\Metadata\EntityDescriptor',
            'EntitiesDescriptor' => '\LightSaml\Model\Metadata\EntitiesDescriptor',
        );

        $rootElementName = $node->localName;

        if (array_key_exists($rootElementName, $map)) {
            if ($class = $map[$rootElementName]) {
                /** @var SamlElementInterface $result */
                $result = new $class();
            } else {
                throw new \LogicException('Deserialization of %s root element is not implemented');
            }
        } else {
            throw new LightSamlXmlException(sprintf("Unknown SAML metadata '%s'", $rootElementName));
        }

        $result->deserialize($node, $context);

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Action\Profile\Outbound\Message;

use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\Helper\MessageContextHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Model\Assertion\Issuer;
use LightSaml\SamlConstants;

/**
 * Sets the Issuer of the outbound message to the value of own entityID.
 */
class CreateMessageIssuerAction extends AbstractProfileAction
{
    /**
     * @param ProfileContext $context
     *
     * @return void
     */
    protected function doExecute(ProfileContext $context)
    {
        $ownEntityDescriptor = $context->getOwnEntityDescriptor();

        $issuer = new Issuer($ownEntityDescriptor->getEntityID());
        $issuer->setFormat(SamlConstants::NAME_ID_FORMAT_ENTITY);

        MessageContextHelper::asSamlMessage($context->getOutboundContext())
            ->setIssuer($issuer);

        $this->logger->debug(
            sprintf('Issuer set to "%s"', $ownEntityDescriptor->getEntityID()),
            LogHelper::getActionContext($context, $this)
        );
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Store\EntityDescriptor;

use LightSaml\Model\Metadata\EntityDescriptor;

class CompositeEntityDescriptorStore implements EntityDescriptorStoreInterface
{
    /** @var EntityDescriptorStoreInterface[] */
    private $children = [];

    /**
     * @param EntityDescriptorStoreInterface[] $stores
     */
    public function __construct(array $stores = array())
    {
        foreach ($stores as $store) {
            $this->add($store);
        }
    }

    /**
     * @param EntityDescriptorStoreInterface $store
     *
     * @return CompositeEntityDescriptorStore This instance
     */
    public function add(EntityDescriptorStoreInterface $store)
    {
        $this->children[] = $store;

        return $this;
    }

    /**
     * @param string $entityId
     *
     * @return EntityDescriptor|null
     */
    public function get($entityId)
    {
        foreach ($this->children as $store) {
            $result = $store->get($entityId);
            if ($result) {
                return $result;
            }
        }

        return null;
    }

    /**
     * @param string $entityId
     *
     * @return bool
     */
    public function has($entityId)
    {
        foreach ($this->children as $store) {
            if ($store->has($entityId)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return array|EntityDescriptor[]
     */
    public function all()
    {
        $result = array();
        foreach ($this->children as $store) {
            $result = array_merge($result, $store->all());
        }

        return $result;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Action\Profile\Outbound\Message;

use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Error\LightSamlContextException;
use LightSaml\Model\Metadata\EndpointReference;
use LightSaml\Model\Metadata\IdpSsoDescriptor;
use LightSaml\Model\Metadata\SpSsoDescriptor;
use LightSaml\Model\Protocol\AuthnRequest;
use LightSaml\Resolver\Endpoint\Criteria\BindingCriteria;
use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria;
use LightSaml\Resolver\Endpoint\Criteria\IndexCriteria;
use LightSaml\Resolver\Endpoint\Criteria\LocationCriteria;
use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria;
use LightSaml\Resolver\Endpoint\EndpointResolverInterface;
use LightSaml\Criteria\CriteriaSet;
use LightSaml\SamlConstants;
use Psr\Log\LoggerInterface;

/**
 * Determines to which endpoint outbound message will be sent.
 */
abstract class ResolveEndpointBaseAction extends AbstractProfileAction
{
    /** @var EndpointResolverInterface */
    protected $endpointResolver;

    /**
     * @param LoggerInterface           $logger
     * @param EndpointResolverInterface $endpointResolver
     */
    public function __construct(LoggerInterface $logger, EndpointResolverInterface $endpointResolver)
    {
        parent::__construct($logger);

        $this->endpointResolver = $endpointResolver;
    }

    /**
     * @param ProfileContext $context
     */
    protected function doExecute(ProfileContext $context)
    {
        if ($context->getEndpointContext()->getEndpoint()) {
            $this->logger->debug(
                sprintf(
                    'Endpoint already set with location "%s" and binding "%s"',
                    $context->getEndpoint()->getLocation(),
                    $context->getEndpoint()->getBinding()
                ),
                LogHelper::getActionContext($context, $this, array(
                    'endpointLocation' => $context->getEndpoint()->getLocation(),
                    'endpointBinding' => $context->getEndpoint()->getBinding(),
                ))
            );

            return;
        }

        $criteriaSet = $this->getCriteriaSet($context);

        $message = $context->getInboundContext()->getMessage();
        if ($message instanceof AuthnRequest) {
            if (null !== $message->getAssertionConsumerServiceIndex()) {
                $criteriaSet->add(new IndexCriteria($message->getAssertionConsumerServiceIndex()));
            }
            if (null !== $message->getAssertionConsumerServiceURL()) {
                $criteriaSet->add(new LocationCriteria($message->getAssertionConsumerServiceURL()));
            }
        }

        $candidates = $this->endpointResolver->resolve($criteriaSet, $context->getPartyEntityDescriptor()->getAllEndpoints());
        /** @var EndpointReference $endpointReference */
        $endpointReference = array_shift($candidates);

        if (null == $endpointReference) {
            $message = sprintf(
                "Unable to determine endpoint for entity '%s'",
                $context->getPartyEntityDescriptor()->getEntityID()
            );
            $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this));
            throw new LightSamlContextException($context, $message);
        }

        $this->logger->debug(
            sprintf(
                'Endpoint resolved to location "%s" and binding "%s"',
                $endpointReference->getEndpoint()->getLocation(),
                $endpointReference->getEndpoint()->getBinding()
            ),
            LogHelper::getActionContext($context, $this, array(
                'endpointLocation' => $endpointReference->getEndpoint()->getLocation(),
                'endpointBinding' => $endpointReference->getEndpoint()->getBinding(),
            ))
        );

        $context->getEndpointContext()->setEndpoint($endpointReference->getEndpoint());
    }

    /**
     * @param ProfileContext $context
     *
     * @return CriteriaSet
     */
    protected function getCriteriaSet(ProfileContext $context)
    {
        $criteriaSet = new CriteriaSet();

        $bindings = $this->getBindings($context);
        if ($bindings) {
            $criteriaSet->add(new BindingCriteria($bindings));
        }

        $descriptorType = $this->getDescriptorType($context);
        if ($descriptorType) {
            $criteriaSet->add(new DescriptorTypeCriteria($descriptorType));
        }

        $serviceType = $this->getServiceType($context);
        if ($serviceType) {
            $criteriaSet->add(new ServiceTypeCriteria($serviceType));
        }

        return $criteriaSet;
    }

    /**
     * @param ProfileContext $context
     *
     * @return string[]
     */
    protected function getBindings(ProfileContext $context)
    {
        return array(
            SamlConstants::BINDING_SAML2_HTTP_POST,
            SamlConstants::BINDING_SAML2_HTTP_REDIRECT,
        );
    }

    /**
     * @param ProfileContext $context
     *
     * @return string|null
     */
    protected function getDescriptorType(ProfileContext $context)
    {
        return ProfileContext::ROLE_IDP == $context->getOwnRole()
            ? SpSsoDescriptor::class
            : IdpSsoDescriptor::class;
    }

    /**
     * @param ProfileContext $context
     *
     * @return string|null
     */
    protected function getServiceType(ProfileContext $context)
    {
        return;
    }
}


<?php

/*
 * This file is part of the LightSAML-Core package.
 *
 * (c) Milos Tomic <tmilos@lightsaml.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace LightSaml\Bridge\Pimple\Container\Factory;

use LightSaml\Bridge\Pimple\Container\OwnContainer;
use LightSaml\Credential\CredentialInterface;
use LightSaml\Error\LightSamlBuildException;
use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface;
use Pimple\Container;
use Pimple\ServiceProviderInterface;

class OwnContainerProvider implements ServiceProviderInterface
{
    /** @var CredentialInterface[] */
    private $ownCredentials = array();

    /** @var EntityDescriptorProviderInterface */
    private $ownEntityDescriptorProvider;

    /**
     * @param EntityDescriptorProviderInterface $ownEntityDescriptorProvider
     * @param CredentialInterface[]             $ownCredentials
     */
    public function __construct(EntityDescriptorProviderInterface $ownEntityDescriptorProvider, array $ownCredentials = null)
    {
        $this->ownEntityDescriptorProvider = $ownEntityDescriptorProvider;
        if ($ownCredentials) {
            foreach ($ownCredentials as $credential) {
                $this->addOwnCredential($credential);
            }
        }
    }

    /**
     * @param CredentialInterface $credential
     *
     * @return OwnContainerProvider
     */
    public function addOwnCredential(CredentialInterface $credential)
    {
        if (null == $credential->getPrivateKey()) {
            throw new LightSamlBuildException('Own credential must have private key');
        }

        $this->ownCredentials[] = $credential;

        return $this;
    }

    /**
     * @param Container $pimple A container instance
     */
    public function register(Container $pimple)
    {
        $pimple[OwnContainer::OWN_CREDENTIALS] = function () {
            return $this->ownCredentials;
        };

        $pimple[OwnContainer::OWN_ENTITY_DESCRIPTOR_PROVIDER] = function () {
            return $this->ownEntityDescriptorProvider;
        };
    }
}