commit ac227fd8f78eab921840392e34ad9631643e9c37 Author: Lars Vierbergen Date: Fri Aug 25 15:00:12 2017 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/AuthserverOAuthAccountBundle.php b/AuthserverOAuthAccountBundle.php new file mode 100644 index 0000000..fba15ab --- /dev/null +++ b/AuthserverOAuthAccountBundle.php @@ -0,0 +1,67 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle; + + +use App\Plugin\Event\ContainerConfigEvent; +use App\Plugin\Event\GetBundlesEvent; +use App\Plugin\PluginEvents; +use HWI\Bundle\OAuthBundle\HWIOAuthBundle; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use vierbergenlars\AuthserverExternalAccountBundle\AuthserverExternalAccountBundle; +use vierbergenlars\AuthserverOAuthAccountBundle\DependencyInjection\AuthserverOAuthAccountExtension; + +class AuthserverOAuthAccountBundle extends Bundle implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return [ + PluginEvents::INITIALIZE_BUNDLES => 'onInitializeBundles', + PluginEvents::CONTAINER_CONFIG => ['onContainerConfig', -20], + ]; + } + + public function onContainerConfig(ContainerConfigEvent $event) + { + $event->getConfigManipulator('[security][firewalls][public]') + ->prependConfig(['oauth' => []]); + + } + + public function onInitializeBundles(GetBundlesEvent $event) + { + $event->addBundle(new AuthserverExternalAccountBundle()); + $event->addBundle(new HWIOAuthBundle()); + } + + public function getContainerExtension() + { + return new AuthserverOAuthAccountExtension(); + } + + public function getParent() + { + return 'HWIOAuthBundle'; + } + + +} diff --git a/Controller/ConnectController.php b/Controller/ConnectController.php new file mode 100644 index 0000000..115c67d --- /dev/null +++ b/Controller/ConnectController.php @@ -0,0 +1,63 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\Controller; + +use HWI\Bundle\OAuthBundle\Controller\ConnectController as BaseConnectController; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser; +use vierbergenlars\AuthserverOAuthAccountBundle\DependencyInjection\AuthserverOAuthAccountExtension; + +class ConnectController extends BaseConnectController +{ + protected function render($view, array $parameters = [], Response $response = null) + { + if($view === 'HWIOAuthBundle:Connect:connect_success.html.twig') + return $this->redirectToRoute('user_profile'); + + $resourceOwnerConfig = $this->container->get(AuthserverOAuthAccountExtension::RESOURCE_OWNER_MAP_SERVICE); + + return parent::render($view, $parameters + ['resourceOwnerConfig' => $resourceOwnerConfig], $response); + } + + public function disconnectServiceAction(Request $request, ExternalUser $externalUser) + { + if($externalUser->getUser() !== $this->getUser()) + throw $this->createAccessDeniedException(); + + $form = $this->createForm(FormType::class); + $form->handleRequest($request); + + if($form->isSubmitted() && $form->isValid()) { + $this->container->get('hwi_oauth.account.connector')->disconnect($externalUser); + + return $this->redirectToRoute('user_profile'); + } + + return $this->render('AuthserverOAuthAccountBundle:Connect:disconnect_service.html.twig', [ + 'externalUser' => $externalUser, + 'form' => $form->createView(), + ]); + + } +} diff --git a/DependencyInjection/AuthserverOAuthAccountExtension.php b/DependencyInjection/AuthserverOAuthAccountExtension.php new file mode 100644 index 0000000..6b5e142 --- /dev/null +++ b/DependencyInjection/AuthserverOAuthAccountExtension.php @@ -0,0 +1,92 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\DependencyInjection; + + +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; +use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerConfig; + +class AuthserverOAuthAccountExtension extends Extension implements PrependExtensionInterface +{ + const RESOURCE_OWNER_MAP_SERVICE = 'vierbergenlars.authserver_oauth_account.resource_owner_map'; + const USER_PROVIDER_SERVICE = 'vierbergenlars.authserver_oauth_account.user_provider'; + + public function prepend(ContainerBuilder $container) + { + $container->prependExtensionConfig('hwi_oauth', [ + 'firewall_names' => ['public'], + 'connect' => [ + 'account_connector' => self::USER_PROVIDER_SERVICE, + ] + ]); + + $configs = $container->getExtensionConfig($this->getAlias()); + + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(), $configs); + $container->prependExtensionConfig('hwi_oauth', [ + 'resource_owners' => array_map(function($resource_owner) { + return (new ResourceOwnerConfig($resource_owner))->getHwiConfig(); + }, $config['resource_owners']), + ]); + + $container->loadFromExtension('security', [ + 'firewalls' => [ + 'public' => [ + 'oauth' => [ + 'resource_owners' => array_combine(array_keys($config['resource_owners']), array_map(function($roName) { + return '/login/oauth/'.$roName; + }, array_keys($config['resource_owners']))), + 'login_path' => 'app_login', + 'failure_path' => 'app_login', + 'oauth_user_provider' => [ + 'service' => self::USER_PROVIDER_SERVICE, + ], + ], + ] + ], + ]); + } + + public function load(array $configs, ContainerBuilder $container) + { + $servicesDirectory = __DIR__.'/../Resources/config'; + $fileLocator = new FileLocator($servicesDirectory); + $xmlLoader = new Loader\XmlFileLoader($container, $fileLocator); + $xmlLoader->load('services.xml'); + + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(), $configs); + + $container->getDefinition(self::RESOURCE_OWNER_MAP_SERVICE) + ->setArgument(0, $config['resource_owners']); + } + + public function getAlias() + { + return 'oauth'; + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..0649397 --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,78 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This is the class that validates and merges configuration from your app/config files + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('oauth'); + + + $rootNode->children() + ->arrayNode('resource_owners') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->arrayNode('config') + ->ignoreExtraKeys(false) + ->end() + ->scalarNode('service_name')->isRequired()->end() + ->scalarNode('icon')->defaultNull()->end() + ->arrayNode('login_button') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('label')->defaultNull()->end() + ->scalarNode('style')->defaultValue('default')->end() + ->scalarNode('icon')->defaultNull()->end() + ->end() + ->end() + ->arrayNode('connect_button') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('label')->defaultNull()->end() + ->scalarNode('style')->defaultValue('default')->end() + ->scalarNode('icon')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ; + + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + return $treeBuilder; + } +} diff --git a/EventListener/LoginButtonListener.php b/EventListener/LoginButtonListener.php new file mode 100644 index 0000000..f943720 --- /dev/null +++ b/EventListener/LoginButtonListener.php @@ -0,0 +1,66 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\EventListener; + + +use HWI\Bundle\OAuthBundle\Security\OAuthUtils; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use vierbergenlars\AuthserverExternalAccountBundle\Event\LoginButtonEvent; +use vierbergenlars\AuthserverExternalAccountBundle\ExternalAccountEvents; +use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerMap; + +class LoginButtonListener implements EventSubscriberInterface +{ + /** + * @var OAuthUtils + */ + private $oauthUtils; + + /** + * @var ResourceOwnerMap + */ + private $resourceOwnerConfig; + + public function __construct(OAuthUtils $oauthUtils, ResourceOwnerMap $resourceOwnerConfig) + { + + $this->oauthUtils = $oauthUtils; + $this->resourceOwnerConfig = $resourceOwnerConfig; + } + + public static function getSubscribedEvents() + { + return [ + ExternalAccountEvents::LOGIN_BUTTON => 'onLoginButton' + ]; + } + + public function onLoginButton(LoginButtonEvent $event) + { + foreach($this->oauthUtils->getResourceOwners() as $owner) + { + $event->addButton($this->resourceOwnerConfig[$owner]->getLoginButton() + [ + 'url' => $this->oauthUtils->getLoginUrl(new Request(), $owner), + ]); + } + } +} diff --git a/EventListener/ProfileTemplateListener.php b/EventListener/ProfileTemplateListener.php new file mode 100644 index 0000000..204ad46 --- /dev/null +++ b/EventListener/ProfileTemplateListener.php @@ -0,0 +1,100 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\EventListener; + + +use App\Event\TemplateEvent; +use HWI\Bundle\OAuthBundle\Security\OAuthUtils; +use Symfony\Bridge\Doctrine\ManagerRegistry; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use User\UserEvents; +use vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser; +use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerMap; + +class ProfileTemplateListener implements EventSubscriberInterface +{ + /** + * @var ManagerRegistry + */ + private $registry; + + /** + * @var ResourceOwnerMap + */ + private $resourceOwnerConfig; + + /** + * @var OAuthUtils + */ + private $oAuthUtils; + + public function __construct(ManagerRegistry $registry, ResourceOwnerMap $resourceOwnerConfig, OAuthUtils $oAuthUtils) + { + $this->registry = $registry; + $this->resourceOwnerConfig = $resourceOwnerConfig; + $this->oAuthUtils = $oAuthUtils; + } + + public static function getSubscribedEvents() + { + return [ + UserEvents::USER_PROFILE_VIEW => [ + ['addOAuthAccountsTop', -50], + ['addOAuthAccounts', -60], + ['addOAuthAccountsBottom', -70], + ], + ]; + } + + public function addOAuthAccountsTop(TemplateEvent $event) + { + $event->addTemplate(new TemplateReference('AuthserverOAuthAccountBundle', 'Profile', 'oauth_accounts_top', 'html', 'twig')); + } + + + public function addOAuthAccounts(TemplateEvent $event) + { + $externalUsers = $this->registry->getManagerForClass(ExternalUser::class) + ->getRepository(ExternalUser::class) + ->findBy([ + 'user' => $event->getSubject(), + ]); + + foreach($this->oAuthUtils->getResourceOwners() as $resourceOwner) { + /* @var $resourceOwner string */ + $roExternalUsers = array_filter($externalUsers, function(ExternalUser $externalUser) use($resourceOwner) { + return $externalUser->getProvider() === $resourceOwner; + }); + $event->addTemplate(new TemplateReference('AuthserverOAuthAccountBundle', 'Profile', 'oauth_account', 'html', 'twig'), [ + 'externalUsers' => $roExternalUsers, + 'resourceOwnerConfig' => $this->resourceOwnerConfig[$resourceOwner], + 'resourceOwner' => $resourceOwner, + ]); + } + } + + public function addOAuthAccountsBottom(TemplateEvent $event) + { + $event->addTemplate(new TemplateReference('AuthserverOAuthAccountBundle', 'Profile', 'oauth_accounts_bottom', 'html', 'twig')); + } + +} diff --git a/ResourceOwner/ResourceOwnerConfig.php b/ResourceOwner/ResourceOwnerConfig.php new file mode 100644 index 0000000..e3fd3d0 --- /dev/null +++ b/ResourceOwner/ResourceOwnerConfig.php @@ -0,0 +1,76 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner; + + +class ResourceOwnerConfig +{ + /** + * @var array + */ + private $config; + + public function __construct(array $config) + { + + $this->config = $config; + } + + public function getHwiConfig() + { + return $this->config['config']; + } + + public function getServiceName() + { + return $this->config['service_name']; + } + + public function getIcon() + { + return $this->config['icon']; + } + + public function getLoginButton() + { + $config = $this->config['login_button']; + foreach([ + 'icon' => $this->getIcon(), + 'label' => $this->getServiceName().' Login', + ] as $k=>$v) + $config[$k] = $config[$k]?:$v; + + return $config; + } + + public function getConnectButton() + { + $config = $this->config['connect_button']; + foreach([ + 'icon' => $this->getIcon(), + 'label' => 'Connect with '.$this->getServiceName(), + ] as $k=>$v) + $config[$k] = $config[$k]?:$v; + + return $config; + } + +} diff --git a/ResourceOwner/ResourceOwnerMap.php b/ResourceOwner/ResourceOwnerMap.php new file mode 100644 index 0000000..7af6988 --- /dev/null +++ b/ResourceOwner/ResourceOwnerMap.php @@ -0,0 +1,58 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner; + +class ResourceOwnerMap implements \ArrayAccess, \IteratorAggregate +{ + private $config; + + public function __construct(array $config) + { + $this->config = $config; + } + + public function offsetExists($offset) + { + return isset($this->config[$offset]); + } + + public function offsetGet($offset) + { + if(!$this->config[$offset] instanceof ResourceOwnerConfig) + $this->config[$offset] = new ResourceOwnerConfig($this->config[$offset]); + return $this->config[$offset]; + } + + public function offsetSet($offset, $value) + { + throw new \LogicException(sprintf('%s is frozen.', self::class)); + } + + public function offsetUnset($offset) + { + throw new \LogicException(sprintf('%s is frozen.', self::class)); + } + + public function getIterator() + { + return new ResourceOwnerMapIterator($this, array_keys($this->config)); + } +} diff --git a/ResourceOwner/ResourceOwnerMapIterator.php b/ResourceOwner/ResourceOwnerMapIterator.php new file mode 100644 index 0000000..feb9ecb --- /dev/null +++ b/ResourceOwner/ResourceOwnerMapIterator.php @@ -0,0 +1,77 @@ +. + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner; + +class ResourceOwnerMapIterator implements \Iterator +{ + /** + * @var ResourceOwnerMap + */ + private $resourceOwnerMap; + + /** + * @var string[] + */ + private $keySet; + + /** + * @var int + */ + private $index = 0; + + /** + * ResourceOwnerMapIterator constructor. + * + * @param ResourceOwnerMap $resourceOwnerMap + * @param \string[] $keySet + */ + public function __construct(ResourceOwnerMap $resourceOwnerMap, array $keySet) + { + $this->resourceOwnerMap = $resourceOwnerMap; + $this->keySet = $keySet; + } + + public function current() + { + return $this->resourceOwnerMap->offsetGet($this->keySet[$this->index]); + } + + public function next() + { + $this->index++; + } + + public function key() + { + return $this->keySet[$this->index]; + } + + public function valid() + { + return isset($this->keySet[$this->index]); + } + + public function rewind() + { + $this->index = 0; + } + +} diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml new file mode 100644 index 0000000..bcce1c7 --- /dev/null +++ b/Resources/config/routing.yml @@ -0,0 +1,16 @@ +hwi_oauth_redirect: + resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml" + prefix: /login/oauth-connect + +hwi_oauth_connect: + resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml" + prefix: /usr/oauth/connect + +vierbergenlars_oauth_account_disconnect: + path: /usr/oauth/disconnect/{externalUser} + defaults: + _controller: AuthserverOAuthAccountBundle:Connect:disconnectService + +oauth_login_paths: + resource: vierbergenlars.authserver_oauth_account.route_provider:getOAuthLoginPaths + type: service diff --git a/Resources/config/services.xml b/Resources/config/services.xml new file mode 100644 index 0000000..2167e66 --- /dev/null +++ b/Resources/config/services.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/views/Connect/connect_confirm.html.twig b/Resources/views/Connect/connect_confirm.html.twig new file mode 100644 index 0000000..1269e12 --- /dev/null +++ b/Resources/views/Connect/connect_confirm.html.twig @@ -0,0 +1,32 @@ +{% extends '::base.html.twig' %} +{% block title %}{{ parent() }} - Connect Account{% endblock %} +{% block body %} +
+
+
+
+
+

{{ 'header.connecting' | trans({}, 'HWIOAuthBundle')}}

+
+
+

{{ 'connect.confirm.text' | trans({'%service%': resourceOwnerConfig[service].serviceName | trans({}, 'HWIOAuthBundle'), '%name%': userInformation.realName}, 'HWIOAuthBundle') }}

+

+ {{ form_start(form, {'action': path('hwi_oauth_connect_service', {'service': service, 'key': key})}) }} + {{ form_widget(form) }} +

+ + {{ 'connect.confirm.cancel' | trans({}, 'HWIOAuthBundle') }} +
+ {{ form_end(form) }} +

+
+ {% if userInformation.profilePicture is defined and userInformation.profilePicture is not empty %} +
+ +
+ {% endif %} +
+
+
+
+{% endblock %} diff --git a/Resources/views/Connect/connect_success.html.twig b/Resources/views/Connect/connect_success.html.twig new file mode 100644 index 0000000..0d1f7cf --- /dev/null +++ b/Resources/views/Connect/connect_success.html.twig @@ -0,0 +1,5 @@ +{% extends 'HWIOAuthBundle::layout.html.twig' %} + +{% block hwi_oauth_content %} +

{{ 'header.success' | trans({'%name%': userInformation.realName}, 'HWIOAuthBundle') }}

+{% endblock hwi_oauth_content %} diff --git a/Resources/views/Connect/disconnect_service.html.twig b/Resources/views/Connect/disconnect_service.html.twig new file mode 100644 index 0000000..33009ea --- /dev/null +++ b/Resources/views/Connect/disconnect_service.html.twig @@ -0,0 +1,27 @@ +{% extends '::base.html.twig' %} +{% block title %}{{ parent() }} - Disconnect Account{% endblock %} +{% block body %} +
+
+
+
+
+

Disconnecting

+
+
+

Are you sure you want to disconnect your {{ resourceOwnerConfig[externalUser.provider].serviceName }} account "{{ externalUser.providerFriendlyName }}"?

+

+ {{ form_start(form) }} + {{ form_widget(form) }} +

+ + Cancel +
+ {{ form_end(form) }} +

+
+
+
+
+
+{% endblock %} diff --git a/Resources/views/Profile/oauth_account.html.twig b/Resources/views/Profile/oauth_account.html.twig new file mode 100644 index 0000000..36240bd --- /dev/null +++ b/Resources/views/Profile/oauth_account.html.twig @@ -0,0 +1,24 @@ +{# @var resourceOwnerConfig \vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerConfig #} +{# @var externalUsers \vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser[] #} +

+ {% if resourceOwnerConfig.icon %} + {{ icon(resourceOwnerConfig.icon) }} + {% endif %} + {{ resourceOwnerConfig.serviceName }} +

+ +{% for externalUser in externalUsers %} + Connected to {{ externalUser.providerFriendlyName }} + {{ icon('chain-broken') }} Disconnect account +
+{% endfor %} + +{% set buttonConfig = resourceOwnerConfig.connectButton %} + + + {% if buttonConfig.icon %} + {{ icon(buttonConfig.icon) }} + {% endif %} + {{ buttonConfig.label }} + diff --git a/Resources/views/Profile/oauth_accounts_bottom.html.twig b/Resources/views/Profile/oauth_accounts_bottom.html.twig new file mode 100644 index 0000000..ebc6381 --- /dev/null +++ b/Resources/views/Profile/oauth_accounts_bottom.html.twig @@ -0,0 +1,2 @@ + + diff --git a/Resources/views/Profile/oauth_accounts_top.html.twig b/Resources/views/Profile/oauth_accounts_top.html.twig new file mode 100644 index 0000000..0ded044 --- /dev/null +++ b/Resources/views/Profile/oauth_accounts_top.html.twig @@ -0,0 +1,5 @@ +
+
+

External authentication sources

+
+
diff --git a/Routing/RouteProvider.php b/Routing/RouteProvider.php new file mode 100644 index 0000000..a3b7c67 --- /dev/null +++ b/Routing/RouteProvider.php @@ -0,0 +1,60 @@ +. + */ + +/** + * Created by PhpStorm. + * User: lars + * Date: 27/08/17 + * Time: 20:38 + */ + +namespace vierbergenlars\AuthserverOAuthAccountBundle\Routing; + + +use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMap; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouteProvider +{ + /** + * @var ResourceOwnerMap + */ + private $resourceOwnerMap; + + public function __construct(ResourceOwnerMap $resourceOwnerMap) + { + $this->resourceOwnerMap = $resourceOwnerMap; + } + + public function getOAuthLoginPaths() + { + $routeCollection = new RouteCollection(); + + foreach($this->resourceOwnerMap->getResourceOwners() as $key =>$resourceOwner) { + /* @var $resourceOwner \HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface */ + $route = new Route('/login/oauth/'.$key); + $routeCollection->add('vl_authserver_oauth_account_'.$key, $route); + } + + return $routeCollection; + } + +} diff --git a/Security/Core/User/OAuthUserProvider.php b/Security/Core/User/OAuthUserProvider.php new file mode 100644 index 0000000..30429fc --- /dev/null +++ b/Security/Core/User/OAuthUserProvider.php @@ -0,0 +1,124 @@ +. + */ + + +namespace vierbergenlars\AuthserverOAuthAccountBundle\Security\Core\User; + + +use App\Entity\User; +use App\Security\User\UserProvider; +use Doctrine\Common\Persistence\ManagerRegistry; +use HWI\Bundle\OAuthBundle\Connect\AccountConnectorInterface; +use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface; +use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException; +use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser; + +class OAuthUserProvider extends UserProvider implements OAuthAwareUserProviderInterface, AccountConnectorInterface +{ + /** + * @var ManagerRegistry + */ + private $registry; + + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry); + $this->registry = $registry; + } + + + /** + * Loads the user by a given UserResponseInterface object. + * + * @param UserResponseInterface $response + * + * @return UserInterface + * + * @throws AccountNotLinkedException if the provider is not linked to an account + */ + public function loadUserByOAuthUserResponse(UserResponseInterface $response) + { + return $this->getExternalAccount($response)->getUser(); + } + + public function connect(UserInterface $user, UserResponseInterface $response) + { + if(!$user instanceof User) + throw new \UnexpectedValueException('User must be instance of '.User::class.', got '.get_class($username)); + try { + $externalUser = $this->getExternalAccount($response); + $this->disconnect($externalUser); + } catch(AccountNotLinkedException $ex) { + // do nothing + } + $externalUser = new ExternalUser(); + $externalUser->setUser($user); + $externalUser->setProvider($response->getResourceOwner()->getName()); + $externalUser->setProviderRef($response->getUsername()); + $externalUser->setProviderFriendlyName($response->getRealName()); + $this->getManager()->persist($externalUser); + $this->getManager()->flush(); + } + + public function disconnect(ExternalUser $externalUser) + { + $this->getManager()->remove($externalUser); + $this->getManager()->flush(); + } + + /** + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + private function getRepo() + { + return $this->getManager()->getRepository('AuthserverExternalAccountBundle:ExternalUser'); + } + + /** + * @return \Doctrine\Common\Persistence\ObjectManager|null + */ + private function getManager() + { + return $this->registry->getManagerForClass('AuthserverExternalAccountBundle:ExternalUser'); + } + + /** + * @param UserResponseInterface $response + * @return ExternalUser + */ + private function getExternalAccount(UserResponseInterface $response) + { + $repo = $this->getRepo(); + + $externalAccount = $repo->findOneBy([ + 'provider' => $response->getResourceOwner()->getName(), + 'provider_ref' => $response->getUsername() + ]); + + if(!$externalAccount) { + throw new AccountNotLinkedException(sprintf('No external account registered for provider "%s", ref: "%s"', $response->getResourceOwner()->getName(), $response->getUsername())); + } + + return $externalAccount; + } + +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0d09a1c --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "vierbergenlars/authserver-oauth-account-bundle", + "require": { + "hwi/oauth-bundle": "^0.5.3" + }, + "autoload": { + "psr-4": { + "vierbergenlars\\AuthserverOAuthAccountBundle\\": "." + } + }, + "license": "AGPL", + "authors": [ + { + "name": "Lars Vierbergen", + "email": "vierbergenlars@gmail.com" + } + ] +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..17994bf --- /dev/null +++ b/composer.lock @@ -0,0 +1,2266 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "206011a2e28f4ab1a92c66cf9fc18bab", + "packages": [ + { + "name": "doctrine/cache", + "version": "v1.6.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2017-07-22T12:49:21+00:00" + }, + { + "name": "hwi/oauth-bundle", + "version": "0.5.3", + "source": { + "type": "git", + "url": "https://github.com/hwi/HWIOAuthBundle.git", + "reference": "50f4bcbe5c217cfdf0f7f40a174b87199b76d1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hwi/HWIOAuthBundle/zipball/50f4bcbe5c217cfdf0f7f40a174b87199b76d1e1", + "reference": "50f4bcbe5c217cfdf0f7f40a174b87199b76d1e1", + "shasum": "" + }, + "require": { + "kriswallsmith/buzz": "~0.13", + "php": "^5.3.3|^7.0", + "symfony/form": "^2.3|^3.0", + "symfony/framework-bundle": "^2.3|^3.0", + "symfony/options-resolver": "^2.3|^3.0", + "symfony/security-bundle": "^2.3|^3.0", + "symfony/templating": "^2.7|^3.0", + "symfony/yaml": "^2.3|^3.0" + }, + "conflict": { + "twig/twig": "<1.12" + }, + "require-dev": { + "doctrine/orm": "^2.3", + "friendsofphp/php-cs-fixer": "^2.0", + "friendsofsymfony/user-bundle": "^1.3|^2.0", + "phpunit/phpunit": "^4.8|^5.0", + "symfony/phpunit-bridge": "^2.7|^3.0", + "symfony/property-access": "^2.3|^3.0", + "symfony/stopwatch": "^2.5|^3.0", + "symfony/twig-bundle": "^2.3|^3.0", + "symfony/validator": "^2.3|^3.0" + }, + "suggest": { + "doctrine/doctrine-bundle": "to use Doctrine user provider", + "friendsofsymfony/user-bundle": "to connect FOSUB with this bundle", + "symfony/property-access": "to use FOSUB integration with this bundle", + "symfony/twig-bundle": "to use the Twig hwi_oauth_* functions" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "0.5-dev" + } + }, + "autoload": { + "psr-4": { + "HWI\\Bundle\\OAuthBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/hwi/HWIOAuthBundle/contributors" + }, + { + "name": "Joseph Bielawski", + "email": "stloyd@gmail.com" + }, + { + "name": "Alexander", + "email": "iam.asm89@gmail.com" + }, + { + "name": "Geoffrey Bachelet", + "email": "geoffrey.bachelet@gmail.com" + } + ], + "description": "Support for authenticating users using both OAuth1.0a and OAuth2 in Symfony2.", + "homepage": "http://github.com/hwi/HWIOAuthBundle", + "keywords": [ + "37signals", + "Authentication", + "Deezer", + "EVE Online", + "amazon", + "asana", + "auth0", + "azure", + "bitbucket", + "bitly", + "box", + "bufferapp", + "clever", + "dailymotion", + "deviantart", + "discogs", + "disqus", + "dropbox", + "eventbrite", + "facebook", + "firewall", + "fiware", + "flickr", + "foursquare", + "github", + "google", + "hubic", + "instagram", + "jawbone", + "jira", + "linkedin", + "mail.ru", + "oauth", + "oauth1", + "oauth2", + "odnoklassniki", + "paypal", + "qq", + "reddit", + "runkeeper", + "salesforce", + "security", + "sensio connect", + "sina weibo", + "slack", + "sound cloud", + "spotify", + "stack exchange", + "stereomood", + "strava", + "toshl", + "trakt", + "trello", + "twitch", + "twitter", + "vkontakte", + "wechat", + "windows live", + "wordpress", + "wunderlist", + "xing", + "yahoo", + "yandex", + "youtube" + ], + "time": "2017-01-08T14:13:58+00:00" + }, + { + "name": "kriswallsmith/buzz", + "version": "v0.15.1", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/Buzz.git", + "reference": "d59932b335c2f2f3ec9eee66a37db6bd50d39e13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/Buzz/zipball/d59932b335c2f2f3ec9eee66a37db6bd50d39e13", + "reference": "d59932b335c2f2f3ec9eee66a37db6bd50d39e13", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.3" + }, + "suggest": { + "ext-curl": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Buzz\\": "lib/Buzz" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Lightweight HTTP client", + "homepage": "https://github.com/kriswallsmith/Buzz", + "keywords": [ + "curl", + "http client" + ], + "time": "2017-08-19T09:43:47+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.10", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-03-13T16:27:32+00:00" + }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24", + "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-01-02T13:31:39+00:00" + }, + { + "name": "symfony/cache", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "cf1ad9191c3d2176c81e7fcc6ea32603186ceddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/cf1ad9191c3d2176c81e7fcc6ea32603186ceddc", + "reference": "cf1ad9191c3d2176c81e7fcc6ea32603186ceddc", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.3" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcuAdapter on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "time": "2017-07-23T08:41:58+00:00" + }, + { + "name": "symfony/class-loader", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "386a294d621576302e7cc36965d6ed53b8c73c4f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/386a294d621576302e7cc36965d6ed53b8c73c4f", + "reference": "386a294d621576302e7cc36965d6ed53b8c73c4f", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2017-06-02T09:51:43+00:00" + }, + { + "name": "symfony/config", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/54ee12b0dd60f294132cabae6f5da9573d2e5297", + "reference": "54ee12b0dd60f294132cabae6f5da9573d2e5297", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/filesystem": "~2.8|~3.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3", + "symfony/finder": "~3.3", + "symfony/yaml": "~3.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2017-07-19T07:37:29+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/7c13ae8ce1e2adbbd574fc39de7be498e1284e13", + "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-07-28T15:27:31+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8d70987f991481e809c63681ffe8ce3f3fde68a0", + "reference": "8d70987f991481e809c63681ffe8ce3f3fde68a0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.1", + "symfony/finder": "<3.3", + "symfony/yaml": "<3.3" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/yaml": "~3.3" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2017-07-28T15:27:31+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "67535f1e3fd662bdc68d7ba317c93eecd973617e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/67535f1e3fd662bdc68d7ba317c93eecd973617e", + "reference": "67535f1e3fd662bdc68d7ba317c93eecd973617e", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2017-06-09T14:53:08+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "427987eb4eed764c3b6e38d52a0f87989e010676" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/427987eb4eed764c3b6e38d52a0f87989e010676", + "reference": "427987eb4eed764c3b6e38d52a0f87989e010676", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-07-11T07:17:58+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-06-01T21:01:25+00:00" + }, + { + "name": "symfony/form", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/form.git", + "reference": "c48dde2c79c37fbbc66af63c37434f0a9b23e979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/form/zipball/c48dde2c79c37fbbc66af63c37434f0a9b23e979", + "reference": "c48dde2c79c37fbbc66af63c37434f0a9b23e979", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/intl": "^2.8.18|^3.2.5", + "symfony/options-resolver": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "~2.8|~3.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/dependency-injection": "<3.3", + "symfony/doctrine-bridge": "<2.7", + "symfony/framework-bundle": "<2.7", + "symfony/http-kernel": "<3.3.5", + "symfony/twig-bridge": "<2.7" + }, + "require-dev": { + "doctrine/collections": "~1.0", + "symfony/config": "~2.7|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/http-kernel": "^3.3.5", + "symfony/security-csrf": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/validator": "^2.8.18|^3.2.5", + "symfony/var-dumper": "~3.3" + }, + "suggest": { + "symfony/framework-bundle": "For templating with PHP.", + "symfony/security-csrf": "For protecting forms against CSRF attacks.", + "symfony/twig-bridge": "For templating with Twig.", + "symfony/validator": "For form validation." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Form\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Form Component", + "homepage": "https://symfony.com", + "time": "2017-07-29T21:53:09+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "d7bbf2dbb4d5f50c227b79d0720af9cd70a4d725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d7bbf2dbb4d5f50c227b79d0720af9cd70a4d725", + "reference": "d7bbf2dbb4d5f50c227b79d0720af9cd70a4d725", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.0", + "ext-xml": "*", + "php": ">=5.5.9", + "symfony/cache": "~3.3", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.3", + "symfony/dependency-injection": "~3.3", + "symfony/event-dispatcher": "^3.3.1", + "symfony/filesystem": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/http-foundation": "~3.3", + "symfony/http-kernel": "~3.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "~3.3", + "symfony/stopwatch": "~2.8|~3.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.0", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.3", + "symfony/form": "<3.3", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/translation": "<3.2", + "symfony/validator": "<3.3", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0", + "sensio/framework-extra-bundle": "^3.0.2", + "symfony/asset": "~3.3", + "symfony/browser-kit": "~2.8|~3.0", + "symfony/console": "~3.3", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/form": "~3.3", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0", + "symfony/property-info": "~3.3", + "symfony/security": "~2.8|~3.0", + "symfony/security-core": "~3.2", + "symfony/security-csrf": "~2.8|~3.0", + "symfony/serializer": "~3.3", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~3.2", + "symfony/validator": "~3.3", + "symfony/web-link": "~3.3", + "symfony/workflow": "~3.3", + "symfony/yaml": "~3.2", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony FrameworkBundle", + "homepage": "https://symfony.com", + "time": "2017-07-26T06:42:48+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "49e8cd2d59a7aa9bfab19e46de680c76e500a031" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/49e8cd2d59a7aa9bfab19e46de680c76e500a031", + "reference": "49e8cd2d59a7aa9bfab19e46de680c76e500a031", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2017-07-21T11:04:46+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "db10d05f1d95e4168e638db7a81c79616f568ea5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/db10d05f1d95e4168e638db7a81c79616f568ea5", + "reference": "db10d05f1d95e4168e638db7a81c79616f568ea5", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0", + "symfony/debug": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~3.3" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.3", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/var-dumper": "~3.3" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/class-loader": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2017-08-01T10:25:59+00:00" + }, + { + "name": "symfony/inflector", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/inflector.git", + "reference": "aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/inflector/zipball/aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb", + "reference": "aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Inflector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Inflector Component", + "homepage": "https://symfony.com", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string", + "symfony", + "words" + ], + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/intl", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "a310b29a794578f544305f4f48e3f6c7981b606c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/a310b29a794578f544305f4f48e3f6c7981b606c", + "reference": "a310b29a794578f544305f4f48e3f6c7981b606c", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-intl-icu": "~1.0" + }, + "require-dev": { + "symfony/filesystem": "~2.8|~3.0" + }, + "suggest": { + "ext-intl": "to use the component with locales other than \"en\"" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "time": "2017-06-01T21:01:25+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ff48982d295bcac1fd861f934f041ebc73ae40f0", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "4aa0b65dc71a7369c1e7e6e2a3ca027d9decdb09" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/4aa0b65dc71a7369c1e7e6e2a3ca027d9decdb09", + "reference": "4aa0b65dc71a7369c1e7e6e2a3ca027d9decdb09", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0|~4.0" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-14T15:44:48+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803", + "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-14T15:44:48+00:00" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "e85ebdef569b84e8709864e1a290c40f156b30ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/e85ebdef569b84e8709864e1a290c40f156b30ca", + "reference": "e85ebdef569b84e8709864e1a290c40f156b30ca", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-14T15:44:48+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "b6482e68974486984f59449ecea1fbbb22ff840f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/b6482e68974486984f59449ecea1fbbb22ff840f", + "reference": "b6482e68974486984f59449ecea1fbbb22ff840f", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-14T15:44:48+00:00" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "67925d1cf0b84bd234a83bebf26d4eb281744c6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/67925d1cf0b84bd234a83bebf26d4eb281744c6d", + "reference": "67925d1cf0b84bd234a83bebf26d4eb281744c6d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2017-07-05T15:09:33+00:00" + }, + { + "name": "symfony/property-access", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "4cd2bc4afdfd914ad18cec97bb4159fc403384ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/4cd2bc4afdfd914ad18cec97bb4159fc403384ea", + "reference": "4cd2bc4afdfd914ad18cec97bb4159fc403384ea", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/inflector": "~3.1", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "symfony/cache": "~3.1" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony PropertyAccess Component", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "time": "2017-07-03T08:12:02+00:00" + }, + { + "name": "symfony/routing", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "4aee1a917fd4859ff8b51b9fd1dfb790a5ecfa26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/4aee1a917fd4859ff8b51b9fd1dfb790a5ecfa26", + "reference": "4aee1a917fd4859ff8b51b9fd1dfb790a5ecfa26", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/yaml": "~3.3" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "time": "2017-07-21T17:43:13+00:00" + }, + { + "name": "symfony/security", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/security.git", + "reference": "0150da00dc697e277cd7e0ffcc633e1e11b584a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security/zipball/0150da00dc697e277cd7e0ffcc633e1e11b584a4", + "reference": "0150da00dc697e277cd7e0ffcc633e1e11b584a4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/http-kernel": "~3.3", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "symfony/property-access": "~2.8|~3.0" + }, + "replace": { + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/ldap": "~3.1", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.8|~3.0", + "symfony/validator": "^2.8.18|^3.2.5" + }, + "suggest": { + "symfony/expression-language": "For using the expression voter", + "symfony/form": "", + "symfony/ldap": "For using the LDAP user and authentication providers", + "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", + "symfony/validator": "For using the user password constraint" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component", + "homepage": "https://symfony.com", + "time": "2017-07-29T21:27:59+00:00" + }, + { + "name": "symfony/security-bundle", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-bundle.git", + "reference": "82a4f52e55864059ac28d38a21b12a86a47236b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/82a4f52e55864059ac28d38a21b12a86a47236b5", + "reference": "82a4f52e55864059ac28d38a21b12a86a47236b5", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": ">=5.5.9", + "symfony/dependency-injection": "~3.3", + "symfony/http-kernel": "~3.3", + "symfony/polyfill-php70": "~1.0", + "symfony/security": "~3.3" + }, + "conflict": { + "symfony/var-dumper": "<3.3" + }, + "require-dev": { + "doctrine/doctrine-bundle": "~1.4", + "symfony/asset": "~2.8|~3.0", + "symfony/browser-kit": "~2.8|~3.0", + "symfony/console": "~3.2", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/form": "^2.8.18|^3.2.5", + "symfony/framework-bundle": "^3.2.8", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/twig-bridge": "~2.8|~3.0", + "symfony/twig-bundle": "~2.8|~3.0", + "symfony/validator": "^3.2.5", + "symfony/var-dumper": "~3.3", + "symfony/yaml": "~2.8|~3.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "symfony/security-acl": "For using the ACL functionality of this bundle" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SecurityBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony SecurityBundle", + "homepage": "https://symfony.com", + "time": "2017-07-22T18:52:59+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/602a15299dc01556013b07167d4f5d3a60e90d15", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/templating", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/templating.git", + "reference": "503b8be934aa98ffeb1dcb144557c42ef3341024" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/templating/zipball/503b8be934aa98ffeb1dcb144557c42ef3341024", + "reference": "503b8be934aa98ffeb1dcb144557c42ef3341024", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "psr/log": "~1.0" + }, + "suggest": { + "psr/log": "For using debug logging in loaders" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Templating\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Templating Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ddc23324e6cfe066f3dd34a37ff494fa80b617ed", + "reference": "ddc23324e6cfe066f3dd34a37ff494fa80b617ed", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-07-23T12:43:26+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +}