<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Service;

use Assert\Assertion;
use Jose\Checker\ClaimCheckerInterface;
use Jose\Checker\HeaderCheckerInterface;

final class CheckerManager
{
    /**
     * @var \Jose\Checker\ClaimCheckerInterface[]
     */
    private $claim_checkers = [];

    /**
     * @var \Jose\Checker\HeaderCheckerInterface[]
     */
    private $header_checkers = [];

    /**
     * @param \Jose\Checker\ClaimCheckerInterface $checker
     * @param string                              $alias
     */
    public function addClaimChecker(ClaimCheckerInterface $checker, $alias)
    {
        if (!array_key_exists($alias, $this->claim_checkers)) {
            $this->claim_checkers[$alias] = $checker;
        }
    }

    /**
     * @param \Jose\Checker\HeaderCheckerInterface $checker
     * @param string                               $alias
     */
    public function addHeaderChecker(HeaderCheckerInterface $checker, $alias)
    {
        if (!array_key_exists($alias, $this->header_checkers)) {
            $this->header_checkers[$alias] = $checker;
        }
    }

    /**
     * @param string[] $selected_claim_checkers
     *
     * @return \Jose\Checker\ClaimCheckerInterface[]
     */
    public function getSelectedClaimChecker(array $selected_claim_checkers)
    {
        $result = [];
        foreach ($selected_claim_checkers as $alias) {
            Assertion::keyExists($this->claim_checkers, $alias, sprintf('The claim checker alias "%s" is not supported.', $alias));
            $result[] = $this->claim_checkers[$alias];
        }

        return $result;
    }

    /**
     * @param string[] $selected_header_checkers
     *
     * @return \Jose\Checker\HeaderCheckerInterface[]
     */
    public function getSelectedHeaderChecker(array $selected_header_checkers)
    {
        $result = [];
        foreach ($selected_header_checkers as $alias) {
            Assertion::keyExists($this->header_checkers, $alias, sprintf('The header checker alias "%s" is not supported.', $alias));
            $result[] = $this->header_checkers[$alias];
        }

        return $result;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Service;

use Jose\Checker\CheckerManagerInterface;
use Jose\Decrypter;
use Jose\DecrypterInterface;
use Jose\Encrypter;
use Jose\EncrypterInterface;
use Jose\Factory\CheckerManagerFactory;
use Jose\JWTCreator;
use Jose\JWTLoader;
use Jose\Signer;
use Jose\SignerInterface;
use Jose\Verifier;
use Jose\VerifierInterface;

final class ServiceFactory
{
    /**
     * @var \SpomkyLabs\JoseBundle\Service\AlgorithmManager
     */
    private $algorithm_manager;

    /**
     * @var \SpomkyLabs\JoseBundle\Service\CompressionManager
     */
    private $compression_manager;

    /**
     * @var \SpomkyLabs\JoseBundle\Service\CheckerManager
     */
    private $checker_manager;

    /**
     * ServiceFactory constructor.
     *
     * @param \SpomkyLabs\JoseBundle\Service\AlgorithmManager   $algorithm_manager
     * @param \SpomkyLabs\JoseBundle\Service\CompressionManager $compression_manager
     * @param \SpomkyLabs\JoseBundle\Service\CheckerManager     $checker_manager
     */
    public function __construct(AlgorithmManager $algorithm_manager, CompressionManager $compression_manager, CheckerManager $checker_manager)
    {
        $this->algorithm_manager = $algorithm_manager;
        $this->compression_manager = $compression_manager;
        $this->checker_manager = $checker_manager;
    }

    /**
     * @param string[] $selected_key_encryption_algorithms
     * @param string[] $selected_content_encryption_algorithms
     * @param string[] $selected_compression_methods
     *
     * @return \Jose\EncrypterInterface
     */
    public function createEncrypter(array $selected_key_encryption_algorithms, array $selected_content_encryption_algorithms, array $selected_compression_methods)
    {
        $key_encryption_algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_key_encryption_algorithms);
        $content_encryption_algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_content_encryption_algorithms);
        $compression_methods = $this->compression_manager->getSelectedCompressionMethods($selected_compression_methods);

        return Encrypter::createEncrypter($key_encryption_algorithms, $content_encryption_algorithms, $compression_methods);
    }

    /**
     * @param string[] $selected_key_encryption_algorithms
     * @param string[] $selected_content_encryption_algorithms
     * @param string[] $selected_compression_methods
     *
     * @return \Jose\DecrypterInterface
     */
    public function createDecrypter(array $selected_key_encryption_algorithms, array $selected_content_encryption_algorithms, array $selected_compression_methods)
    {
        $key_encryption_algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_key_encryption_algorithms);
        $content_encryption_algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_content_encryption_algorithms);
        $compression_methods = $this->compression_manager->getSelectedCompressionMethods($selected_compression_methods);

        return Decrypter::createDecrypter($key_encryption_algorithms, $content_encryption_algorithms, $compression_methods);
    }

    /**
     * @param string[] $selected_algorithms
     *
     * @return \Jose\SignerInterface
     */
    public function createSigner(array $selected_algorithms)
    {
        $algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_algorithms);

        return Signer::createSigner($algorithms);
    }

    /**
     * @param string[] $selected_algorithms
     *
     * @return \Jose\VerifierInterface
     */
    public function createVerifier(array $selected_algorithms)
    {
        $algorithms = $this->algorithm_manager->getSelectedAlgorithmMethods($selected_algorithms);

        return Verifier::createVerifier($algorithms);
    }

    /**
     * @param string[] $selected_claim_checkers
     * @param string[] $selected_header_checkers
     *
     * @return \Jose\Checker\CheckerManagerInterface
     */
    public function createChecker(array $selected_claim_checkers, array $selected_header_checkers)
    {
        $claim_checkers = $this->checker_manager->getSelectedClaimChecker($selected_claim_checkers);
        $header_checkers = $this->checker_manager->getSelectedHeaderChecker($selected_header_checkers);

        return CheckerManagerFactory::createClaimCheckerManager($claim_checkers, $header_checkers);
    }

    /**
     * @param \Jose\Checker\CheckerManagerInterface $checker_manager
     * @param \Jose\VerifierInterface               $verifier
     * @param \Jose\DecrypterInterface|null         $decrypter
     *
     * @return \Jose\JWTLoader
     */
    public function createJWTLoader(CheckerManagerInterface $checker_manager, VerifierInterface $verifier, DecrypterInterface $decrypter = null)
    {
        $jwt_loader = new JWTLoader($checker_manager, $verifier);
        if (null !== $decrypter) {
            $jwt_loader->enableDecryptionSupport($decrypter);
        }

        return $jwt_loader;
    }

    /**
     * @param \Jose\SignerInterface         $signer
     * @param \Jose\EncrypterInterface|null $encrypter
     *
     * @return \Jose\JWTCreator
     */
    public function createJWTCreator(SignerInterface $signer, EncrypterInterface $encrypter = null)
    {
        $jwt_creator = new JWTCreator($signer);
        if (null !== $encrypter) {
            $jwt_creator->enableEncryptionSupport($encrypter);
        }

        return $jwt_creator;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class EasyJWTLoaderSource implements SourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'easy_jwt_loader';
    }

    /**
     * {@inheritdoc}
     */
    public function createService($name, array $config, ContainerBuilder $container)
    {
    }

    /**
     * {@inheritdoc}
     */
    public function getNodeDefinition(ArrayNodeDefinition $node)
    {
        $node
            ->children()
                ->arrayNode('easy_jwt_loader')
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->children()
                            ->booleanNode('is_public')
                                ->info('If true, the service will be public, else private.')
                                ->defaultTrue()
                            ->end()
                            ->arrayNode('signature_algorithms')
                                ->useAttributeAsKey('name')
                                ->isRequired()
                                ->cannotBeEmpty()
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('key_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('content_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('compression_methods')
                                ->useAttributeAsKey('name')
                                ->defaultValue(['DEF'])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('claim_checkers')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('header_checkers')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container, array $config)
    {
        if (false === array_key_exists($this->getName(), $config)) {
            return;
        }

        foreach ($config[$this->getName()] as $id => $section) {
            $config = $this->createServiceConfiguration($config, $id, $section);
        }

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createServiceConfiguration(array $config, $id, array $section)
    {
        $config = $this->createVerifierServiceConfiguration($config, $id, $section);
        $config = $this->createDecrypterServiceConfiguration($config, $id, $section);
        $config = $this->createJWTLoaderServiceConfiguration($config, $id, $section);
        $config = $this->createCheckerServiceConfiguration($config, $id, $section);

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createVerifierServiceConfiguration(array $config, $id, array $section)
    {
        $config['verifiers'] = array_merge(
            array_key_exists('verifiers', $config) ? $config['verifiers'] : [],
            [$id => [
                'is_public'  => $section['is_public'],
                'algorithms' => $section['signature_algorithms'],
            ]]
        );

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createDecrypterServiceConfiguration(array $config, $id, array $section)
    {
        if (false === $this->isEncryptionSupportEnabled($section)) {
            return $config;
        }
        $config['decrypters'] = array_merge(
            array_key_exists('decrypters', $config) ? $config['decrypters'] : [],
            [$id => [
                'is_public'                     => $section['is_public'],
                'key_encryption_algorithms'     => $section['key_encryption_algorithms'],
                'content_encryption_algorithms' => $section['content_encryption_algorithms'],
                'compression_methods'           => $section['compression_methods'],
            ]]
        );

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createCheckerServiceConfiguration(array $config, $id, array $section)
    {
        $config['checkers'] = array_merge(
            array_key_exists('checkers', $config) ? $config['checkers'] : [],
            [$id => [
                'is_public' => $section['is_public'],
                'claims'    => $section['claim_checkers'],
                'headers'   => $section['header_checkers'],
            ]]
        );

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createJWTLoaderServiceConfiguration(array $config, $id, array $section)
    {
        $service = [
            'is_public' => $section['is_public'],
            'verifier'  => sprintf('jose.verifier.%s', $id),
            'checker'   => sprintf('jose.checker.%s', $id),
        ];
        if (true === $this->isEncryptionSupportEnabled($section)) {
            $service['decrypter'] = sprintf('jose.decrypter.%s', $id);
        }
        $config['jwt_loaders'] = array_merge(
            array_key_exists('jwt_loaders', $config) ? $config['jwt_loaders'] : [],
            [$id => $service]
        );

        return $config;
    }

    /**
     * @param array $section
     *
     * @return bool
     */
    private function isEncryptionSupportEnabled(array $section)
    {
        if (true === empty($section['key_encryption_algorithms']) && true === empty($section['content_encryption_algorithms'])) {
            return false;
        }

        if (true === empty($section['key_encryption_algorithms']) || true === empty($section['content_encryption_algorithms'])) {
            throw new \LogicException('Both key encryption algorithms and content encryption algorithms must be set to enable the encryption support.');
        }

        return true;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSource;

use Symfony\Component\Config\Definition\Builder\NodeDefinition;

final class RandomECKey extends RandomKey
{
    /**
     * {@inheritdoc}
     */
    protected function getKeyConfig(array $config)
    {
        $values = $config['key_configuration'];
        $values['kty'] = 'EC';
        $values['crv'] = $config['curve'];

        return $values;
    }

    /**
     * {@inheritdoc}
     */
    public function getKey()
    {
        return 'ec';
    }

    /**
     * {@inheritdoc}
     */
    public function addConfiguration(NodeDefinition $node)
    {
        $node
            ->children()
                ->scalarNode('curve')
                    ->validate()
                        ->ifTrue(self::checkCurve())
                        ->thenInvalid('Unsupported curve. Please use "P-256", "P-384" or "P-521".')
                    ->end()
                    ->isRequired()
                ->end()
            ->end();
        parent::addConfiguration($node);
    }

    /**
     * @return \Closure
     */
    private static function checkCurve()
    {
        return function ($v) {
            return !in_array($v, ['P-256', 'P-384', 'P-521']);
        };
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSource;

use SpomkyLabs\JoseBundle\DependencyInjection\Source\AbstractSource;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class JWK extends AbstractSource implements JWKSourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function createDefinition(ContainerBuilder $container, array $config)
    {
        $definition = new Definition('Jose\Object\JWK');
        $definition->setFactory([
            new Reference('jose.factory.jwk'),
            'createFromValues',
        ]);
        $definition->setArguments([
            json_decode($config['value'], true),
        ]);

        return $definition;
    }

    /**
     * {@inheritdoc}
     */
    public function getKey()
    {
        return 'jwk';
    }

    /**
     * {@inheritdoc}
     */
    public function addConfiguration(NodeDefinition $node)
    {
        parent::addConfiguration($node);
        $node
            ->children()
                ->scalarNode('value')->isRequired()->end()
            ->end();
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Helper;

use Assert\Assertion;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * This helper will help you to create services configuration.
 */
final class ConfigurationHelper
{
    const BUNDLE_ALIAS = 'jose';

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $header_checkers
     * @param string[]                                                $claim_checkers
     * @param bool                                                    $is_public
     */
    public static function addChecker(ContainerBuilder $container, $name, array $header_checkers, array $claim_checkers, $is_public = true)
    {
        $config = self::getCheckerConfiguration($name, $header_checkers, $claim_checkers, $is_public);
        self::updateJoseConfiguration($container, $config, 'checkers');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $signature_algorithms
     * @param bool                                                    $create_verifier
     * @param bool                                                    $is_public
     */
    public static function addSigner(ContainerBuilder $container, $name, array $signature_algorithms, $create_verifier = false, $is_public = true)
    {
        $config = self::getSignerConfiguration($name, $signature_algorithms, $create_verifier, $is_public);
        self::updateJoseConfiguration($container, $config, 'signers');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $signature_algorithms
     * @param bool                                                    $is_public
     */
    public static function addVerifier(ContainerBuilder $container, $name, array $signature_algorithms, $is_public = true)
    {
        $config = self::getVerifierConfiguration($name, $signature_algorithms, $is_public);
        self::updateJoseConfiguration($container, $config, 'verifiers');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $key_encryption_algorithms
     * @param string[]                                                $content_encryption_algorithms
     * @param string[]                                                $compression_methods
     * @param bool                                                    $create_decrypter
     * @param bool                                                    $is_public
     */
    public static function addEncrypter(ContainerBuilder $container, $name, array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF'], $create_decrypter = false, $is_public = true)
    {
        $config = self::getEncrypterConfiguration($name, $key_encryption_algorithms, $content_encryption_algorithms, $compression_methods, $create_decrypter, $is_public);
        self::updateJoseConfiguration($container, $config, 'encrypters');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $key_encryption_algorithms
     * @param string[]                                                $content_encryption_algorithms
     * @param string[]                                                $compression_methods
     * @param bool                                                    $is_public
     */
    public static function addDecrypter(ContainerBuilder $container, $name, array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF'], $is_public = true)
    {
        $config = self::getDecrypterConfiguration($name, $key_encryption_algorithms, $content_encryption_algorithms, $compression_methods, $is_public);
        self::updateJoseConfiguration($container, $config, 'decrypters');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string                                                  $signer
     * @param string|null                                             $encrypter
     * @param bool                                                    $is_public
     */
    public static function addJWTCreator(ContainerBuilder $container, $name, $signer, $encrypter = null, $is_public = true)
    {
        $config = self::getJWTCreatorConfiguration($name, $signer, $encrypter, $is_public);
        self::updateJoseConfiguration($container, $config, 'jwt_creators');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string                                                  $verifier
     * @param string                                                  $checker
     * @param string|null                                             $decrypter
     * @param bool                                                    $is_public
     */
    public static function addJWTLoader(ContainerBuilder $container, $name, $verifier, $checker, $decrypter = null, $is_public = true)
    {
        $config = self::getJWTLoaderConfiguration($name, $verifier, $checker, $decrypter, $is_public);
        self::updateJoseConfiguration($container, $config, 'jwt_loaders');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string                                                  $storage_path
     * @param int                                                     $nb_keys
     * @param array                                                   $key_configuration
     * @param bool                                                    $is_rotatable
     * @param bool                                                    $is_public
     */
    public static function addRandomJWKSet(ContainerBuilder $container, $name, $storage_path, $nb_keys, array $key_configuration, $is_rotatable = false, $is_public = true)
    {
        $config = self::getRandomJWKSetConfiguration($name, $storage_path, $nb_keys, $key_configuration, $is_rotatable, $is_public);
        self::updateJoseConfiguration($container, $config, 'key_sets');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string                                                  $jwkset
     * @param bool                                                    $is_public
     */
    public static function addPublicJWKSet(ContainerBuilder $container, $name, $jwkset, $is_public = true)
    {
        $config = self::getPublicJWKSetConfiguration($name, $jwkset, $is_public);
        self::updateJoseConfiguration($container, $config, 'key_sets');
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param string                                                  $name
     * @param string[]                                                $jwksets
     * @param bool                                                    $is_public
     */
    public static function addJWKSets(ContainerBuilder $container, $name, array $jwksets, $is_public = true)
    {
        $config = self::getJWKSetsConfiguration($name, $jwksets, $is_public);
        self::updateJoseConfiguration($container, $config, 'key_sets');
    }

    /**
     * @param string   $name
     * @param string[] $header_checkers
     * @param string[] $claim_checkers
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getCheckerConfiguration($name, array $header_checkers, array $claim_checkers, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::allString($header_checkers);
        Assertion::allString($claim_checkers);

        return [
            self::BUNDLE_ALIAS => [
                'checkers' => [
                    $name => [
                        'is_public' => $is_public,
                        'claims'    => $claim_checkers,
                        'headers'   => $header_checkers,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string   $name
     * @param string[] $signature_algorithms
     * @param bool     $create_verifier
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getSignerConfiguration($name, array $signature_algorithms, $create_verifier = false, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::allString($signature_algorithms);
        Assertion::notEmpty($signature_algorithms);
        Assertion::boolean($create_verifier);

        return [
            self::BUNDLE_ALIAS => [
                'signers' => [
                    $name => [
                        'is_public'       => $is_public,
                        'algorithms'      => $signature_algorithms,
                        'create_verifier' => $create_verifier,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string   $name
     * @param string[] $signature_algorithms
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getVerifierConfiguration($name, array $signature_algorithms, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::allString($signature_algorithms);
        Assertion::notEmpty($signature_algorithms);

        return [
            self::BUNDLE_ALIAS => [
                'verifiers' => [
                    $name => [
                        'is_public'  => $is_public,
                        'algorithms' => $signature_algorithms,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string   $name
     * @param string[] $key_encryption_algorithms
     * @param string[] $content_encryption_algorithms
     * @param string[] $compression_methods
     * @param bool     $create_decrypter
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getEncrypterConfiguration($name, array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF'], $create_decrypter = false, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::allString($key_encryption_algorithms);
        Assertion::notEmpty($key_encryption_algorithms);
        Assertion::allString($content_encryption_algorithms);
        Assertion::notEmpty($content_encryption_algorithms);
        Assertion::boolean($create_decrypter);

        return [
            self::BUNDLE_ALIAS => [
                'encrypters' => [
                    $name => [
                        'is_public'                     => $is_public,
                        'key_encryption_algorithms'     => $key_encryption_algorithms,
                        'content_encryption_algorithms' => $content_encryption_algorithms,
                        'compression_methods'           => $compression_methods,
                        'create_decrypter'              => $create_decrypter,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string   $name
     * @param string[] $key_encryption_algorithms
     * @param string[] $content_encryption_algorithms
     * @param string[] $compression_methods
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getDecrypterConfiguration($name, array $key_encryption_algorithms, array $content_encryption_algorithms, array $compression_methods = ['DEF'], $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::allString($key_encryption_algorithms);
        Assertion::notEmpty($key_encryption_algorithms);
        Assertion::allString($content_encryption_algorithms);
        Assertion::notEmpty($content_encryption_algorithms);

        return [
            self::BUNDLE_ALIAS => [
                'decrypters' => [
                    $name => [
                        'is_public'                     => $is_public,
                        'key_encryption_algorithms'     => $key_encryption_algorithms,
                        'content_encryption_algorithms' => $content_encryption_algorithms,
                        'compression_methods'           => $compression_methods,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string      $name
     * @param string      $signer
     * @param string|null $encrypter
     * @param bool        $is_public
     *
     * @return array
     */
    private static function getJWTCreatorConfiguration($name, $signer, $encrypter = null, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::string($signer);
        Assertion::notEmpty($signer);
        Assertion::nullOrString($encrypter);

        return [
            self::BUNDLE_ALIAS => [
                'jwt_creators' => [
                    $name => [
                        'is_public' => $is_public,
                        'signer'    => $signer,
                        'encrypter' => $encrypter,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string      $name
     * @param string      $verifier
     * @param string      $checker
     * @param string|null $decrypter
     * @param bool        $is_public
     *
     * @return array
     */
    private static function getJWTLoaderConfiguration($name, $verifier, $checker, $decrypter = null, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::string($verifier);
        Assertion::notEmpty($verifier);
        Assertion::string($checker);
        Assertion::notEmpty($checker);
        Assertion::nullOrString($decrypter);

        return [
            self::BUNDLE_ALIAS => [
                'jwt_loaders' => [
                    $name => [
                        'is_public' => $is_public,
                        'verifier'  => $verifier,
                        'checker'   => $checker,
                        'decrypter' => $decrypter,
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string $name
     * @param string $storage_path
     * @param int    $nb_keys
     * @param array  $key_configuration
     * @param bool   $is_rotatable
     * @param bool   $is_public
     *
     * @return array
     */
    private static function getRandomJWKSetConfiguration($name, $storage_path, $nb_keys, array $key_configuration, $is_rotatable = false, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::string($storage_path);
        Assertion::notEmpty($storage_path);
        Assertion::integer($nb_keys);
        Assertion::greaterThan($nb_keys, 0);
        Assertion::boolean($is_rotatable);

        return [
            self::BUNDLE_ALIAS => [
                'key_sets' => [
                    $name => [
                        'auto' => [
                            'is_rotatable'      => $is_rotatable,
                            'is_public'         => $is_public,
                            'nb_keys'           => $nb_keys,
                            'key_configuration' => $key_configuration,
                            'storage_path'      => $storage_path,
                        ],
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string $name
     * @param string $jwkset
     * @param bool   $is_public
     *
     * @return array
     */
    private static function getPublicJWKSetConfiguration($name, $jwkset, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::string($jwkset);
        Assertion::notEmpty($jwkset);

        return [
            self::BUNDLE_ALIAS => [
                'key_sets' => [
                    $name => [
                        'public_jwkset' => [
                            'is_public' => $is_public,
                            'id'        => $jwkset,
                        ],
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string   $name
     * @param string[] $jwksets
     * @param bool     $is_public
     *
     * @return array
     */
    private static function getJWKSetsConfiguration($name, array $jwksets, $is_public = true)
    {
        self::checkParameters($name, $is_public);
        Assertion::isArray($jwksets);
        Assertion::allString($jwksets);
        Assertion::allNotEmpty($jwksets);

        return [
            self::BUNDLE_ALIAS => [
                'key_sets' => [
                    $name => [
                        'jwksets' => [
                            'is_public' => $is_public,
                            'id'        => $jwksets,
                        ],
                    ],
                ],
            ],
        ];
    }

    /**
     * @param string $name
     * @param bool   $is_public
     */
    private static function checkParameters($name, $is_public)
    {
        Assertion::string($name);
        Assertion::notEmpty($name);
        Assertion::boolean($is_public);
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param array                                                   $config
     * @param string                                                  $element
     */
    private static function updateJoseConfiguration(ContainerBuilder $container, array $config, $element)
    {
        self::checkJoseBundleEnabled($container);
        $jose_config = current($container->getExtensionConfig(self::BUNDLE_ALIAS));
        if (!isset($jose_config[$element])) {
            $jose_config[$element] = [];
        }
        $jose_config[$element] = array_merge($jose_config[$element], $config[self::BUNDLE_ALIAS][$element]);
        $container->prependExtensionConfig(self::BUNDLE_ALIAS, $jose_config);
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     *
     * @throws \InvalidArgumentException
     */
    private static function checkJoseBundleEnabled(ContainerBuilder $container)
    {
        $bundles = $container->getParameter('kernel.bundles');
        Assertion::keyExists($bundles, 'SpomkyLabsJoseBundle', 'The "Spomky-Labs/JoseBundle" must be enabled.');
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

final class JWKSource implements SourceInterface
{
    /**
     * @var null|\SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSource\JWKSourceInterface[]
     */
    private $jwk_sources = null;

    /**
     * @var string
     */
    private $bundle_path = null;

    /**
     * JWKSetSource constructor.
     *
     * @param string $bundle_path
     */
    public function __construct($bundle_path)
    {
        $this->bundle_path = $bundle_path;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'keys';
    }

    /**
     * {@inheritdoc}
     */
    public function createService($name, array $config, ContainerBuilder $container)
    {
        foreach ($config as $key => $adapter) {
            if (array_key_exists($key, $this->getJWKSources())) {
                $this->getJWKSources()[$key]->create($container, 'key', $name, $adapter);

                return;
            }
        }

        throw new \LogicException(sprintf('The JWK definition "%s" is not configured.', $name));
    }

    /**
     * {@inheritdoc}
     */
    public function getNodeDefinition(ArrayNodeDefinition $node)
    {
        $sourceNodeBuilder = $node
            ->children()
                ->arrayNode('keys')
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->performNoDeepMerging()
                        ->children();
        foreach ($this->getJWKSources() as $name => $source) {
            $sourceNode = $sourceNodeBuilder->arrayNode($name)->canBeUnset();
            $source->addConfiguration($sourceNode);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container, array $config)
    {
    }

    /**
     * @return \SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSource\JWKSourceInterface[]
     */
    private function getJWKSources()
    {
        if (null !== $this->jwk_sources) {
            return $this->jwk_sources;
        }

        // load bundled adapter factories
        $tempContainer = new ContainerBuilder();
        $loader = new XmlFileLoader($tempContainer, new FileLocator($this->bundle_path.'/Resources/config'));
        $loader->load('jwk_sources.xml');

        $services = $tempContainer->findTaggedServiceIds('jose.jwk_source');
        $jwk_sources = [];
        foreach (array_keys($services) as $id) {
            $factory = $tempContainer->get($id);
            $jwk_sources[str_replace('-', '_', $factory->getKey())] = $factory;
        }

        $this->jwk_sources = $jwk_sources;

        return $jwk_sources;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSetSource;

use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class RandomKeySet extends AbstractJWKSetSource
{
    /**
     * {@inheritdoc}
     */
    public function createDefinition(ContainerBuilder $container, array $config)
    {
        if (true === $config['is_rotatable']) {
            $definition = new Definition('Jose\Object\RotatableJWKSet');
            $method = 'createRotatableKeySet';
        } else {
            $definition = new Definition('Jose\Object\StorableJWKSet');
            $method = 'createStorableKeySet';
        }

        $definition->setFactory([
            new Reference('jose.factory.jwk'),
            $method,
        ]);
        $definition->setArguments([
            $config['storage_path'],
            $config['key_configuration'],
            $config['nb_keys'],
        ]);

        return $definition;
    }

    /**
     * {@inheritdoc}
     */
    public function getKeySet()
    {
        return 'auto';
    }

    /**
     * {@inheritdoc}
     */
    public function addConfiguration(NodeDefinition $node)
    {
        parent::addConfiguration($node);
        $node
            ->children()
                ->booleanNode('is_rotatable')
                    ->info('If true, the service will be a rotatable key, else just storable.')
                    ->defaultFalse()
                ->end()
                ->integerNode('nb_keys')
                    ->info('Number of keys in the key set.')
                    ->isRequired()
                    ->min(1)
                ->end()
                ->scalarNode('storage_path')->isRequired()->end()
                ->arrayNode('key_configuration')
                    ->defaultValue([])
                    ->useAttributeAsKey('key')
                    ->prototype('variable')->end()
                ->end()
            ->end();
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Controller;

use Jose\Object\JWKSetInterface;
use Symfony\Component\HttpFoundation\Response;

final class JWKSetController
{
    /**
     * @var \Jose\Object\JWKSetInterface
     */
    private $jwkset;

    /**
     * JWKSetController constructor.
     *
     * @param \Jose\Object\JWKSetInterface $jwkset
     */
    public function __construct(JWKSetInterface $jwkset)
    {
        $this->jwkset = $jwkset;
    }

    /**
     * @param string $_format
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function getAction($_format)
    {
        if ('json' === $_format) {
            $value = json_encode($this->jwkset);
            $ct = 'application/jwk-set+json; charset=UTF-8';
        } else {
            $value = json_encode($this->jwkset->toPEM());
            $ct = 'application/json; charset=UTF-8';
        }

        return new Response($value, Response::HTTP_OK, ['content-type' => $ct]);
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Service;

use Assert\Assertion;
use Jose\Compression\CompressionInterface;

final class CompressionManager
{
    /**
     * @var \Jose\Compression\CompressionInterface[]
     */
    private $compression_methods = [];

    /**
     * @param \Jose\Compression\CompressionInterface $compression_method
     */
    public function addCompressionMethod(CompressionInterface $compression_method)
    {
        $name = $compression_method->getMethodName();
        if (!array_key_exists($name, $this->compression_methods)) {
            $this->compression_methods[$name] = $compression_method;
        }
    }

    /**
     * @param string[] $selected_compression_methods
     *
     * @return \Jose\Compression\CompressionInterface[]
     */
    public function getSelectedCompressionMethods(array $selected_compression_methods)
    {
        $result = [];
        foreach ($selected_compression_methods as $method) {
            Assertion::keyExists($this->compression_methods, $method, sprintf('The compression method "%s" is not supported.', $method));
            $result[] = $this->compression_methods[$method];
        }

        return $result;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class EasyJWTCreatorSource implements SourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'easy_jwt_creator';
    }

    /**
     * {@inheritdoc}
     */
    public function createService($name, array $config, ContainerBuilder $container)
    {
    }

    /**
     * {@inheritdoc}
     */
    public function getNodeDefinition(ArrayNodeDefinition $node)
    {
        $node
            ->children()
                ->arrayNode('easy_jwt_creator')
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->children()
                            ->booleanNode('is_public')
                                ->info('If true, the service will be public, else private.')
                                ->defaultTrue()
                            ->end()
                            ->arrayNode('signature_algorithms')
                                ->useAttributeAsKey('name')
                                ->isRequired()
                                ->cannotBeEmpty()
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('key_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('content_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->defaultValue([])
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('compression_methods')
                                ->useAttributeAsKey('name')
                                ->defaultValue(['DEF'])
                                ->prototype('scalar')->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container, array $config)
    {
        if (false === array_key_exists($this->getName(), $config)) {
            return;
        }

        foreach ($config[$this->getName()] as $id => $section) {
            $config = $this->createServiceConfiguration($config, $id, $section);
        }

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createServiceConfiguration(array $config, $id, array $section)
    {
        $config = $this->createSignerServiceConfiguration($config, $id, $section);
        $config = $this->createEncrypterServiceConfiguration($config, $id, $section);
        $config = $this->createJWTCreatorServiceConfiguration($config, $id, $section);

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createSignerServiceConfiguration(array $config, $id, array $section)
    {
        $config['signers'] = array_merge(
            array_key_exists('signers', $config) ? $config['signers'] : [],
            [$id => [
                'is_public'  => $section['is_public'],
                'algorithms' => $section['signature_algorithms'],
            ]]
        );

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createEncrypterServiceConfiguration(array $config, $id, array $section)
    {
        if (false === $this->isEncryptionSupportEnabled($section)) {
            return $config;
        }
        $config['encrypters'] = array_merge(
            array_key_exists('encrypters', $config) ? $config['encrypters'] : [],
            [$id => [
                'is_public'                     => $section['is_public'],
                'key_encryption_algorithms'     => $section['key_encryption_algorithms'],
                'content_encryption_algorithms' => $section['content_encryption_algorithms'],
                'compression_methods'           => $section['compression_methods'],
            ]]
        );

        return $config;
    }

    /**
     * @param array  $config
     * @param string $id
     * @param array  $section
     *
     * @return array
     */
    private function createJWTCreatorServiceConfiguration(array $config, $id, array $section)
    {
        $service = [
            'is_public' => $section['is_public'],
            'signer'    => sprintf('jose.signer.%s', $id),
        ];
        if (true === $this->isEncryptionSupportEnabled($section)) {
            $service['encrypter'] = sprintf('jose.encrypter.%s', $id);
        }
        $config['jwt_creators'] = array_merge(
            array_key_exists('jwt_creators', $config) ? $config['jwt_creators'] : [],
            [$id => $service]
        );

        return $config;
    }

    /**
     * @param array $section
     *
     * @return bool
     */
    private function isEncryptionSupportEnabled(array $section)
    {
        if (true === empty($section['key_encryption_algorithms']) && true === empty($section['content_encryption_algorithms'])) {
            return false;
        }

        if (true === empty($section['key_encryption_algorithms']) || true === empty($section['content_encryption_algorithms'])) {
            throw new \LogicException('Both key encryption algorithms and content encryption algorithms must be set to enable the encryption support.');
        }

        return true;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Service;

use Assert\Assertion;
use Jose\Algorithm\JWAInterface;

final class AlgorithmManager
{
    /**
     * @var \Jose\Algorithm\JWAInterface[]
     */
    private $algorithms = [];

    /**
     * @param \Jose\Algorithm\JWAInterface $algorithm
     */
    public function addAlgorithm(JWAInterface $algorithm)
    {
        $name = $algorithm->getAlgorithmName();
        if (!array_key_exists($name, $this->algorithms)) {
            $this->algorithms[$name] = $algorithm;
        }
    }

    /**
     * @param string[] $selected_algorithms
     *
     * @return \Jose\Algorithm\JWAInterface[]
     */
    public function getSelectedAlgorithmMethods(array $selected_algorithms)
    {
        $result = [];
        foreach ($selected_algorithms as $algorithm) {
            Assertion::keyExists($this->algorithms, $algorithm, sprintf('The algorithm "%s" is not supported.', $algorithm));
            $result[] = $this->algorithms[$algorithm];
        }

        return $result;
    }
}


<?php

namespace BotMan\Drivers\Twitter\Console\Commands;

use Illuminate\Console\Command;
use Abraham\TwitterOAuth\TwitterOAuth;

class TwitterRegisterCommand extends Command
{
    /**
     * The console command signature.
     *
     * @var string
     */
    protected $signature = 'botman:twitter:register {--output}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Register your bot with Twitter\'s webhook';

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $connection = new TwitterOAuth(
            config('botman.twitter.consumer_key'),
            config('botman.twitter.consumer_secret'),
            config('botman.twitter.token'),
            config('botman.twitter.token_secret')
        );

        $url = $this->ask('What is the target url for the Twitter bot?');

        $this->info('Using '.$url);

        $this->info('Pinging Twitter...');

        /*
         * During the beta, access to the API is provided up to 50 account subscriptions per webhook
         * and up to one webhook per Twitter application.
         *
         * So we check if we already have a webhook defined and delete it.
         */
        $webhooks = $connection->get('account_activity/webhooks');
        if (count($webhooks) > 0) {
            $connection->delete('account_activity/webhooks/'.$webhooks[0]->id);
        }

        $webhook = $connection->post('account_activity/webhooks', [
            'url' => $url
        ]);

        if (isset($webhook->errors)) {
            $this->error('Unable to setup Twitter webhook.');
            dump($webhook);
        } else {
            // Subscribe to the webhook
            $connection->post('account_activity/webhooks/'.$webhook->id.'/subscriptions', []);

            $this->info('Your bot is now set up with Twitter\'s webhook!');
        }

        if ($this->option('output')) {
            dump($webhook);
        }
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Compiler;

use Assert\Assertion;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class CheckerCompilerPass implements CompilerPassInterface
{
    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('jose.checker_manager')) {
            return;
        }

        $definition = $container->getDefinition('jose.checker_manager');

        $this->addClaimCheckers($definition, $container);
        $this->addHeaderCheckers($definition, $container);
    }

    /**
     * @param \Symfony\Component\DependencyInjection\Definition       $definition
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     */
    private function addHeaderCheckers(Definition $definition, ContainerBuilder $container)
    {
        $taggedHeaderCheckerServices = $container->findTaggedServiceIds('jose.checker.header');
        foreach ($taggedHeaderCheckerServices as $id => $tags) {
            foreach ($tags as $attributes) {
                Assertion::keyExists($attributes, 'alias', sprintf("The header checker '%s' does not have any 'alias' attribute.", $id));
                $definition->addMethodCall('addHeaderChecker', [new Reference($id), $attributes['alias']]);
            }
        }
    }

    /**
     * @param \Symfony\Component\DependencyInjection\Definition       $definition
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     */
    private function addClaimCheckers(Definition $definition, ContainerBuilder $container)
    {
        $taggedClaimCheckerServices = $container->findTaggedServiceIds('jose.checker.claim');
        foreach ($taggedClaimCheckerServices as $id => $tags) {
            foreach ($tags as $attributes) {
                Assertion::keyExists($attributes, 'alias', sprintf("The claim checker '%s' does not have any 'alias' attribute.", $id));
                $definition->addMethodCall('addClaimChecker', [new Reference($id), $attributes['alias']]);
            }
        }
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle;

use SpomkyLabs\JoseBundle\DependencyInjection\Compiler\AlgorithmCompilerPass;
use SpomkyLabs\JoseBundle\DependencyInjection\Compiler\CheckerCompilerPass;
use SpomkyLabs\JoseBundle\DependencyInjection\Compiler\CompressionCompilerPass;
use SpomkyLabs\JoseBundle\DependencyInjection\SpomkyLabsJoseBundleExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

final class SpomkyLabsJoseBundle extends Bundle
{
    /**
     * {@inheritdoc}
     */
    public function getContainerExtension()
    {
        return new SpomkyLabsJoseBundleExtension('jose', __DIR__);
    }

    /**
     * {@inheritdoc}
     */
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(new AlgorithmCompilerPass());
        $container->addCompilerPass(new CompressionCompilerPass());
        $container->addCompilerPass(new CheckerCompilerPass());
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class JWTLoaderSource implements SourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'jwt_loaders';
    }

    /**
     * {@inheritdoc}
     */
    public function createService($name, array $config, ContainerBuilder $container)
    {
        $service_id = sprintf('jose.jwt_loader.%s', $name);
        $definition = new Definition('Jose\JWTLoader');
        $definition->setFactory([
            new Reference('jose.factory.service'),
            'createJWTLoader',
        ]);
        $definition->setArguments([
            new Reference($config['checker']),
            new Reference($config['verifier']),
            null === $config['decrypter'] ? null : new Reference($config['decrypter']),
        ]);
        $definition->setPublic($config['is_public']);

        $container->setDefinition($service_id, $definition);
    }

    /**
     * {@inheritdoc}
     */
    public function getNodeDefinition(ArrayNodeDefinition $node)
    {
        $node->children()
                ->arrayNode($this->getName())
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->children()
                            ->booleanNode('is_public')
                                ->info('If true, the service will be public, else private.')
                                ->defaultTrue()
                            ->end()
                            ->scalarNode('verifier')
                                ->isRequired()
                            ->end()
                            ->scalarNode('checker')
                                ->isRequired()
                            ->end()
                            ->scalarNode('decrypter')
                                ->defaultNull()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container, array $config)
    {
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection;

use Assert\Assertion;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

final class SpomkyLabsJoseBundleExtension extends Extension implements PrependExtensionInterface
{
    /**
     * @var string
     */
    private $alias;

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

    /**
     * @var \SpomkyLabs\JoseBundle\DependencyInjection\Source\SourceInterface[]
     */
    private $service_sources = [];

    /**
     * SpomkyLabsJoseBundleExtension constructor.
     *
     * @param string $alias
     * @param string $bundle_path
     */
    public function __construct($alias, $bundle_path)
    {
        $this->alias = $alias;
        $this->bundle_path = $bundle_path;
        $this->addDefaultSources();
    }

    /**
     * @param \SpomkyLabs\JoseBundle\DependencyInjection\Source\SourceInterface $source
     */
    public function addServiceSource(Source\SourceInterface $source)
    {
        $name = $source->getName();
        Assertion::false(in_array($name, $this->service_sources), sprintf('The source "%s" is already set.', $name));
        $this->service_sources[$name] = $source;
    }

    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $processor = new Processor();

        $config = $processor->processConfiguration(
            $this->getConfiguration($configs, $container),
            $configs
        );

        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $services = ['services', 'compression_methods', 'checkers', 'signature_algorithms', 'encryption_algorithms', 'checkers', 'jwkset_controller'];
        foreach ($services as $basename) {
            $loader->load(sprintf('%s.xml', $basename));
        }

        $this->initConfiguration($container, $config);
    }

    /**
     * @return \SpomkyLabs\JoseBundle\DependencyInjection\Configuration
     */
    public function getConfiguration(array $configs, ContainerBuilder $container)
    {
        return new Configuration($this->getAlias(), $this->service_sources);
    }

    /**
     * {@inheritdoc}
     */
    public function getAlias()
    {
        return $this->alias;
    }

    /**
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     * @param array                                                   $config
     */
    private function initConfiguration(ContainerBuilder $container, array $config)
    {
        foreach ($this->service_sources as $service_source) {
            foreach ($config[$service_source->getName()] as $name => $data) {
                $service_source->createService($name, $data, $container);
            }
        }
    }

    private function addDefaultSources()
    {
        $this->addServiceSource(new Source\JWTCreatorSource());
        $this->addServiceSource(new Source\JWTLoaderSource());
        $this->addServiceSource(new Source\SignerSource());
        $this->addServiceSource(new Source\VerifierSource());
        $this->addServiceSource(new Source\EncrypterSource());
        $this->addServiceSource(new Source\DecrypterSource());
        $this->addServiceSource(new Source\CheckerSource());
        $this->addServiceSource(new Source\JWKSource($this->bundle_path));
        $this->addServiceSource(new Source\JWKSetSource($this->bundle_path));
        $this->addServiceSource(new Source\EasyJWTCreatorSource());
        $this->addServiceSource(new Source\EasyJWTLoaderSource());
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container)
    {
        $configs = $container->getExtensionConfig($this->getAlias());
        $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);

        foreach ($this->service_sources as $service_source) {
            $result = $service_source->prepend($container, $config);
            if (null !== $result) {
                $container->prependExtensionConfig($this->getAlias(), $result);
            }
        }
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Command;

use Jose\Object\StorableInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class DeleteCommand extends ContainerAwareCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('spomky-labs:jose:delete')
            ->setDescription('Delete a key or key set.')
            ->addArgument(
                'service',
                InputArgument::REQUIRED
            )
            ->setHelp(<<<'EOT'
The <info>%command.name%</info> command will delete a key or key set.
If the service is called, then the key will be created again.

  <info>php %command.full_name%</info>
EOT
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $service_name = $input->getArgument('service');
        if (!$this->getContainer()->has($service_name)) {
            $output->writeln(sprintf('<error>The service "%s" does not exist.</error>', $service_name));

            return 1;
        }
        $service = $this->getContainer()->get($service_name);
        if (!$service instanceof StorableInterface) {
            $output->writeln(sprintf('<error>The service "%s" is not a storable object.</error>', $service_name));

            return 2;
        }

        $service->delete();
        $output->writeln('Done.');
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

final class Configuration implements ConfigurationInterface
{
    /**
     * @var \SpomkyLabs\JoseBundle\DependencyInjection\Source\SourceInterface[]
     */
    private $service_sources;

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

    /**
     * Configuration constructor.
     *
     * @param string                                                              $alias
     * @param \SpomkyLabs\JoseBundle\DependencyInjection\Source\SourceInterface[] $service_sources
     */
    public function __construct($alias, array $service_sources)
    {
        $this->alias = $alias;
        $this->service_sources = $service_sources;
    }

    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root($this->alias);

        foreach ($this->service_sources as $service_source) {
            $service_source->getNodeDefinition($rootNode);
        }

        return $treeBuilder;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSetSource;

use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class JWKSets extends AbstractJWKSetSource
{
    /**
     * {@inheritdoc}
     */
    public function createDefinition(ContainerBuilder $container, array $config)
    {
        $definition = new Definition('Jose\Object\JWKSet');
        $definition->setFactory([
            new Reference('jose.factory.jwk'),
            'createKeySets',
        ]);
        foreach ($config['id'] as $key_set_id) {
            $ref = new Reference($key_set_id);
            $definition->addMethodCall('addKeySet', [$ref]);
        }

        return $definition;
    }

    /**
     * {@inheritdoc}
     */
    public function getKeySet()
    {
        return 'jwksets';
    }

    /**
     * {@inheritdoc}
     */
    public function addConfiguration(NodeDefinition $node)
    {
        parent::addConfiguration($node);
        $node
            ->children()
                ->arrayNode('id')
                    ->prototype('scalar')->end()
                    ->isRequired()
                ->end()
            ->end();
    }
}


<?php

namespace BotMan\Drivers\Twitter;

use BotMan\BotMan\Users\User;
use Illuminate\Support\Collection;
use BotMan\BotMan\Drivers\HttpDriver;
use Abraham\TwitterOAuth\TwitterOAuth;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Interfaces\VerifiesService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ParameterBag;
use BotMan\BotMan\Messages\Incoming\IncomingMessage;
use BotMan\BotMan\Messages\Outgoing\OutgoingMessage;

class TwitterDriver extends HttpDriver implements VerifiesService
{
    protected $headers = [];

    const DRIVER_NAME = 'Twitter';

    /** @var TwitterOAuth */
    protected $connection;

    /**
     * @param Request $request
     * @return void
     */
    public function buildPayload(Request $request)
    {
        $this->payload = new ParameterBag(json_decode($request->getContent(), true) ?? []);
        $this->headers = $request->headers->all();
        $this->event = Collection::make($this->payload->get('direct_message_events'));
        $this->config = Collection::make($this->config->get('twitter', []));

        $this->connection = new TwitterOAuth(
            $this->config->get('consumer_key'),
            $this->config->get('consumer_secret'),
            $this->config->get('token'),
            $this->config->get('token_secret')
        );
    }

    /**
     * Determine if the request is for this driver.
     *
     * @return bool
     */
    public function matchesRequest()
    {
        if (isset($this->headers['x-twitter-webhooks-signature'])) {

            $signature = $this->headers['x-twitter-webhooks-signature'][0];
            $hash = hash_hmac('sha256', json_encode($this->payload->all()), $this->config->get('consumer_secret'), true);

            return $signature === 'sha256='.base64_encode($hash);
        }
        return false;
    }

    /**
     * Retrieve the chat message(s).
     *
     * @return array
     */
    public function getMessages()
    {
        return $this->event
            ->filter(function ($event) {
                return $event['type'] === 'message_create';
            })
            ->map(function ($event) {
                $message = $event['message_create'];

                return new IncomingMessage($message['message_data']['text'], $message['sender_id'], $message['target']['recipient_id'], $event);
        })->toArray();
    }

    /**
     * @return bool
     */
    public function isConfigured()
    {
        return ! empty($this->config->get('consumer_secret'));
    }

    /**
     * Retrieve User information.
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage
     * @return User
     */
    public function getUser(IncomingMessage $matchingMessage)
    {
        $sender_id = $matchingMessage->getRecipient();

        $user = Collection::make($this->payload->get('users'))->first(function ($user) use ($sender_id) {
            return $user['id'] === $sender_id;
        });

        return new User($user['id'], null, null, $user['name'], $user);
    }

    /**
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $message
     * @return Answer
     */
    public function getConversationAnswer(IncomingMessage $message)
    {
        $payload = $message->getPayload();
        $answer = Answer::create($message->getText())->setMessage($message);

        if(isset($payload['message_create']['message_data']['quick_reply_response']['metadata'])) {
            $answer->setInteractiveReply(true);
            $answer->setValue($payload['message_create']['message_data']['quick_reply_response']['metadata']);
        }

        return $answer;
    }

    /**
     * Convert a Question object into a valid Twitter message object.
     *
     * @param \BotMan\BotMan\Messages\Outgoing\Question $question
     * @return array
     */
    private function convertQuestion(Question $question)
    {
        $buttons = Collection::make($question->getButtons())->map(function ($button) {
            return [
                'label' => $button['text'],
                'metadata' => $button['value']
            ];
        });

        return [
            'text' => $question->getText(),
            'quick_reply' => [
                'type'=>'options',
                'options' => $buttons->toArray(),
            ],
        ];
    }

    /**
     * @param OutgoingMessage|\BotMan\BotMan\Messages\Outgoing\Question $message
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage
     * @param array $additionalParameters
     * @return array
     */
    public function buildServicePayload($message, $matchingMessage, $additionalParameters = [])
    {
        $payload = [
            'event' => [
                'type' => 'message_create',
                'message_create' => [
                    'target' => [
                        'recipient_id' => $matchingMessage->getSender()
                    ],
                    'message_data' => [
                        'text' => ''
                    ],
                ]
            ]
        ];
        if ($message instanceof OutgoingMessage) {
            $payload['event']['message_create']['message_data']['text'] = $message->getText();
        } elseif ($message instanceof Question) {
            $payload['event']['message_create']['message_data'] = $this->convertQuestion($message);
        }

        return $payload;
    }

    /**
     * @param mixed $payload
     * @return Response
     */
    public function sendPayload($payload)
    {
        $this->connection->post('direct_messages/events/new', $payload, true);

        return Response::create($this->connection->getLastBody(), $this->connection->getLastHttpCode());
    }

    /**
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage
     * @return Response
     */
    public function types(IncomingMessage $matchingMessage)
    {
        $this->connection->post('direct_messages/indicate_typing', [
            'recipient_id' => $matchingMessage->getSender()
        ], false);

        return Response::create('', $this->connection->getLastHttpCode());
    }

    /**
     * Low-level method to perform driver specific API requests.
     *
     * @param string $endpoint
     * @param array $parameters
     * @param \BotMan\BotMan\Messages\Incoming\IncomingMessage $matchingMessage
     * @return Response
     */
    public function sendRequest($endpoint, array $parameters, IncomingMessage $matchingMessage)
    {
        $this->connection->post($endpoint, $parameters, true);

        return Response::create($this->connection->getLastBody(), $this->connection->getLastHttpCode());
    }

    /**
     * @param Request $request
     */
    public function verifyRequest(Request $request)
    {
        if (! is_null($request->get('crc_token'))) {
            $hash = hash_hmac('sha256', $request->get('crc_token'), $this->config->get('consumer_secret'), true);

            return Response::create(json_encode([
                'response_token' => 'sha256='.base64_encode($hash)
            ]), 200, [
                'Content-Type' => 'application/json'
            ])->send();
        }
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class EncrypterSource implements SourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'encrypters';
    }

    /**
     * {@inheritdoc}
     */
    public function createService($name, array $config, ContainerBuilder $container)
    {
        $service_id = sprintf('jose.encrypter.%s', $name);
        $definition = new Definition('Jose\Encrypter');
        $definition->setFactory([
            new Reference('jose.factory.service'),
            'createEncrypter',
        ]);
        $definition->setArguments([
            $config['key_encryption_algorithms'],
            $config['content_encryption_algorithms'],
            $config['compression_methods'],
        ]);
        $definition->setPublic($config['is_public']);

        $container->setDefinition($service_id, $definition);
    }

    /**
     * {@inheritdoc}
     */
    public function getNodeDefinition(ArrayNodeDefinition $node)
    {
        $node
            ->children()
                ->arrayNode($this->getName())
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->children()
                        ->booleanNode('is_public')
                            ->info('If true, the service will be public, else private.')
                            ->defaultTrue()
                        ->end()
                            ->arrayNode('key_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->isRequired()
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('content_encryption_algorithms')
                                ->useAttributeAsKey('name')
                                ->isRequired()
                                ->prototype('scalar')->end()
                            ->end()
                            ->arrayNode('compression_methods')
                                ->useAttributeAsKey('name')
                                ->defaultValue(['DEF'])
                                ->prototype('scalar')->end()
                            ->end()
                            ->booleanNode('create_decrypter')
                                ->defaultFalse()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();
    }

    /**
     * {@inheritdoc}
     */
    public function prepend(ContainerBuilder $container, array $config)
    {
        if (false === array_key_exists($this->getName(), $config)) {
            return;
        }

        foreach ($config[$this->getName()] as $id => $section) {
            if (true === $section['create_decrypter']) {
                $values = $section;
                unset($values['create_decrypter']);
                $config['decrypters'] = array_merge(
                    array_key_exists('decrypters', $config) ? $config['decrypters'] : [],
                    [$id => $values]
                );
            }
        }

        return $config;
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSetSource;

use SpomkyLabs\JoseBundle\DependencyInjection\Source\AbstractSource;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

abstract class AbstractJWKSetSource extends AbstractSource implements JWKSetSourceInterface
{
    /**
     * {@inheritdoc}
     */
    public function create(ContainerBuilder $container, $type, $name, array $config)
    {
        parent::create($container, $type, $name, $config);

        if (null !== $config['path']) {
            $jwkset_id = 'jose.key_set.'.$name;
            $controller_definition = new Definition('SpomkyLabs\JoseBundle\Controller\JWKSetController');
            $controller_definition->setFactory([new Reference('jose.controller.jwkset_controllery_factory'), 'createJWKSetController']);
            $controller_definition->setArguments([new Reference($jwkset_id)]);
            $controller_id = 'jose.controller.'.$name;
            $container->setDefinition($controller_id, $controller_definition);

            $jwkset_loader_definition = $container->getDefinition('jose.routing.jwkset_loader');
            $jwkset_loader_definition->addMethodCall('addJWKSetRoute', [$config['path'], $name]);
        }
    }

    /**
     * @param \Symfony\Component\Config\Definition\Builder\NodeDefinition $node
     */
    public function addConfiguration(NodeDefinition $node)
    {
        parent::addConfiguration($node);
        $node
            ->children()
                ->scalarNode('path')
                    ->info('To share the JWKSet, then set a valid path (e.g. "/jwkset.json").')
                    ->defaultNull()
                ->end()
            ->end();
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\Command;

use Jose\Object\JWKSetInterface;
use Jose\Object\RotatableInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class RotateCommand extends ContainerAwareCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('spomky-labs:jose:rotate')
            ->setDescription('Rotate a key or keys in the key set')
            ->addArgument(
                'service',
                InputArgument::REQUIRED
            )
            ->addArgument(
                'ttl',
                InputArgument::OPTIONAL,
                '',
                3600 * 24 * 7
            )
            ->setHelp(<<<'EOT'
The <info>%command.name%</info> command will rotate a key or keys in the key set.

  <info>php %command.full_name%</info>
EOT
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $service_name = $input->getArgument('service');
        if (!$this->getContainer()->has($service_name)) {
            $output->writeln(sprintf('<error>The service "%s" does not exist.</error>', $service_name));

            return 1;
        }
        $service = $this->getContainer()->get($service_name);
        if (!$service instanceof JWKSetInterface) {
            $output->writeln(sprintf('<error>The service "%s" is not a key set.</error>', $service_name));

            return 2;
        }

        if (!$service instanceof RotatableInterface) {
            $output->writeln(sprintf('<error>The service "%s" is not a rotatable key set.</error>', $service_name));

            return 3;
        }

        $mtime = $service->getLastModificationTime();

        if (null === $mtime) {
            $service->regen();
            $output->writeln('Done.');
        } elseif ($mtime + $input->getArgument('ttl') <= time()) {
            $service->rotate();
            $output->writeln('Done.');
        } else {
            $output->writeln(sprintf('The key set "%s" has not expired.', $service_name));
        }
    }
}


<?php

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Spomky-Labs
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

namespace SpomkyLabs\JoseBundle\DependencyInjection\Source\JWKSetSource;

use Symfony\Component\Config\Definition\Builder\NodeDefinition;

abstract class DownloadedJWKSet extends AbstractJWKSetSource
{
    /**
     * {@inheritdoc}
     */
    public function addConfiguration(NodeDefinition $node)
    {
        parent::addConfiguration($node);
        $node
            ->children()
                ->scalarNode('url')->isRequired()->end()
                ->booleanNode('is_secured')->defaultTrue()->end()
                ->booleanNode('is_https')->defaultTrue()->end()
                ->scalarNode('cache')->defaultNull()->end()
                ->integerNode('cache_ttl')->defaultValue(86400)->end()
            ->end();
    }
}


<?php
declare(strict_types=1);

namespace Northwoods\Broker;

use OutOfBoundsException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class Broker implements
    MiddlewareInterface,
    RequestHandlerInterface
{
    /** @var MiddlewareInterface[] */
    private $middleware = [];

    /**
     * Add middleware to the end of the stack
     */
    public function append(MiddlewareInterface ...$middleware): self
    {
        array_push($this->middleware, ...$middleware);

        return $this;
    }

    /**
     * Add middleware to the beginning of the stack
     */
    public function prepend(MiddlewareInterface ...$middleware): self
    {
        array_unshift($this->middleware, ...$middleware);

        return $this;
    }

    // RequestHandlerInterface
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $broker = clone $this;

        return $broker->nextMiddleware()->process($request, $broker);
    }

    // MiddlewareInterface
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        try {
            return $this->handle($request);
        } catch (OutOfBoundsException $e) {
            return $handler->handle($request);
        }
    }

    /**
     * @throws OutOfBoundsException If no middleware is available
     */
    private function nextMiddleware(): MiddlewareInterface
    {
        $middleware = array_shift($this->middleware);

        if ($middleware === null) {
            throw new OutOfBoundsException("End of middleware stack");
        }

        return $middleware;
    }
}