diff --git a/DependencyInjection/AuthserverOAuthAccountExtension.php b/DependencyInjection/AuthserverOAuthAccountExtension.php index f7512c5..d7b58b7 100644 --- a/DependencyInjection/AuthserverOAuthAccountExtension.php +++ b/DependencyInjection/AuthserverOAuthAccountExtension.php @@ -34,14 +34,19 @@ use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerConfi class AuthserverOAuthAccountExtension extends Extension implements PrependExtensionInterface { + const USER_PROVIDER_SERVICE = 'vierbergenlars.authserver_oauth_account.user_provider'; + const RESOURCE_OWNER_MAP_SERVICE = 'vierbergenlars.authserver_oauth_account.resource_owner_map'; + public function prepend(ContainerBuilder $container) { $container->prependExtensionConfig('hwi_oauth', [ - 'firewall_names' => ['public'], + 'firewall_names' => [ + 'public' + ], 'connect' => [ - 'account_connector' => self::USER_PROVIDER_SERVICE, + 'account_connector' => self::USER_PROVIDER_SERVICE ] ]); @@ -50,46 +55,45 @@ class AuthserverOAuthAccountExtension extends Extension implements PrependExtens $processor = new Processor(); $config = $processor->processConfiguration(new Configuration(), $configs); $container->prependExtensionConfig('hwi_oauth', [ - 'resource_owners' => array_map(function($resource_owner) { + 'resource_owners' => array_map(function ($resource_owner) { return $resource_owner['config']; - }, $config['resource_owners']), + }, $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; + '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, - ], - ], + 'service' => self::USER_PROVIDER_SERVICE + ] + ] ] - ], + ] ]); } public function load(array $configs, ContainerBuilder $container) { - $servicesDirectory = __DIR__.'/../Resources/config'; + $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']); - foreach($config['resource_owners'] as $name => $config) - { + foreach ($config['resource_owners'] as $name => $config) { $service = new DefinitionDecorator('vierbergenlars.authserver_oauth_account.external_account_provider.abstract'); $service->replaceArgument(0, $name); - $service->replaceArgument(1, $config); $service->addTag(AuthserverExternalAccountBundle::EXTERNAL_ACCOUNT_PROVIDER_TAG); - $container->setDefinition('vierbergenlars.authserver_oauth_account.external_account_provider.impl.'.$name, $service); + $container->setDefinition('vierbergenlars.authserver_oauth_account.external_account_provider.impl.' . $name, $service); } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 0649397..eb10739 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -17,7 +17,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - namespace vierbergenlars\AuthserverOAuthAccountBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; @@ -30,15 +29,17 @@ use Symfony\Component\Config\Definition\ConfigurationInterface; */ class Configuration implements ConfigurationInterface { + /** - * {@inheritDoc} + * + * {@inheritdoc} */ public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('oauth'); - + // @formatter:off $rootNode->children() ->arrayNode('resource_owners') ->useAttributeAsKey('name') @@ -49,6 +50,7 @@ class Configuration implements ConfigurationInterface ->end() ->scalarNode('service_name')->isRequired()->end() ->scalarNode('icon')->defaultNull()->end() + ->booleanNode('trust_email_verification')->defaultFalse()->end() ->arrayNode('login_button') ->addDefaultsIfNotSet() ->children() @@ -68,7 +70,7 @@ class Configuration implements ConfigurationInterface ->end() ->end() ; - + // @formatter:on // Here you should define the parameters that are allowed to // configure your bundle. See the documentation linked above for diff --git a/EventListener/RegistrationHandlerListener.php b/EventListener/RegistrationHandlerListener.php index 6da310a..6029145 100644 --- a/EventListener/RegistrationHandlerListener.php +++ b/EventListener/RegistrationHandlerListener.php @@ -27,6 +27,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use vierbergenlars\AuthserverOAuthAccountBundle\Entity\TemporaryUser; use Doctrine\ORM\EntityManagerInterface; use App\Entity\EmailAddress; +use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerMap; class RegistrationHandlerListener implements EventSubscriberInterface { @@ -43,6 +44,12 @@ class RegistrationHandlerListener implements EventSubscriberInterface */ private $em; + /** + * + * @var ResourceOwnerMap + */ + private $resourceOwnerMap; + public static function getSubscribedEvents() { return [ @@ -57,10 +64,11 @@ class RegistrationHandlerListener implements EventSubscriberInterface ]; } - public function __construct(EntityManagerInterface $em, TokenStorageInterface $tokenStorage) + public function __construct(EntityManagerInterface $em, TokenStorageInterface $tokenStorage, ResourceOwnerMap $resourceOwnerMap) { $this->em = $em; $this->tokenStorage = $tokenStorage; + $this->resourceOwnerMap = $resourceOwnerMap; } private function getTemporaryUser() @@ -96,6 +104,9 @@ class RegistrationHandlerListener implements EventSubscriberInterface return; /* @var $user \App\Entity\User */ if ($temporaryUser = $this->getTemporaryUser()) { + if (!$this->resourceOwnerMap->getOwnerFromExternalUser($temporaryUser->getExternalUser()) + ->isTrustEmailVerification()) + return; if ($temporaryUser->getEmail() && $temporaryUser->getEmail() === $user->getPrimaryEmailAddress()->getEmail()) { $user->getPrimaryEmailAddress()->setVerified(true); } diff --git a/ExternalAccount/OAuthExternalAccountProvider.php b/ExternalAccount/OAuthExternalAccountProvider.php index 46ceec0..8b87dee 100644 --- a/ExternalAccount/OAuthExternalAccountProvider.php +++ b/ExternalAccount/OAuthExternalAccountProvider.php @@ -17,41 +17,46 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - namespace vierbergenlars\AuthserverOAuthAccountBundle\ExternalAccount; - use HWI\Bundle\OAuthBundle\Security\OAuthUtils; use Symfony\Component\HttpFoundation\Request; use vierbergenlars\AuthserverExternalAccountBundle\ExternalAccount\ExternalAccountProviderInterface; use vierbergenlars\AuthserverExternalAccountBundle\ValueObject\Button; use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerConfig; +use vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerMap; class OAuthExternalAccountProvider implements ExternalAccountProviderInterface { + /** + * * @var ResourceOwnerConfig */ private $resourceOwnerConfig; + /** + * * @var */ private $name; + /** + * * @var OAuthUtils */ private $OAuthUtils; - public function __construct($name, $resourceOwnerConfig, OAuthUtils $OAuthUtils) + public function __construct($name, ResourceOwnerMap $resourceOwnerMap, OAuthUtils $OAuthUtils) { - $this->resourceOwnerConfig = new ResourceOwnerConfig($resourceOwnerConfig); + $this->resourceOwnerConfig = $resourceOwnerMap[$name]; $this->name = $name; $this->OAuthUtils = $OAuthUtils; } public function getName() { - return 'oauth_'.$this->name; + return 'oauth_' . $this->name; } public function getServiceName() @@ -67,15 +72,15 @@ class OAuthExternalAccountProvider implements ExternalAccountProviderInterface public function getLoginButton() { return new Button($this->resourceOwnerConfig->getLoginButton() + [ - 'url' => $this->OAuthUtils->getLoginUrl(new Request(), $this->name), + 'url' => $this->OAuthUtils->getLoginUrl(new Request(), $this->name) ]); } public function getConnectButton() { return new Button($this->resourceOwnerConfig->getConnectButton() + [ - 'url' => $this->OAuthUtils->getLoginUrl(new Request(), $this->name), - ]); + 'url' => $this->OAuthUtils->getLoginUrl(new Request(), $this->name) + ]); } public function hasConnect() diff --git a/ResourceOwner/ResourceOwnerConfig.php b/ResourceOwner/ResourceOwnerConfig.php index e3fd3d0..9b9b5bd 100644 --- a/ResourceOwner/ResourceOwnerConfig.php +++ b/ResourceOwner/ResourceOwnerConfig.php @@ -17,20 +17,19 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - namespace vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner; - class ResourceOwnerConfig { + /** + * * @var array */ private $config; public function __construct(array $config) { - $this->config = $config; } @@ -39,6 +38,11 @@ class ResourceOwnerConfig return $this->config['config']; } + public function isTrustEmailVerification() + { + return $this->config['trust_email_verification']; + } + public function getServiceName() { return $this->config['service_name']; @@ -52,11 +56,11 @@ class ResourceOwnerConfig public function getLoginButton() { $config = $this->config['login_button']; - foreach([ - 'icon' => $this->getIcon(), - 'label' => $this->getServiceName().' Login', - ] as $k=>$v) - $config[$k] = $config[$k]?:$v; + foreach ([ + 'icon' => $this->getIcon(), + 'label' => $this->getServiceName() . ' Login' + ] as $k => $v) + $config[$k] = $config[$k] ?: $v; return $config; } @@ -64,13 +68,12 @@ class ResourceOwnerConfig 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; + 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 index 7af6988..24377dd 100644 --- a/ResourceOwner/ResourceOwnerMap.php +++ b/ResourceOwner/ResourceOwnerMap.php @@ -17,11 +17,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - namespace vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner; +use vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser; + class ResourceOwnerMap implements \ArrayAccess, \IteratorAggregate { + private $config; public function __construct(array $config) @@ -29,6 +31,22 @@ class ResourceOwnerMap implements \ArrayAccess, \IteratorAggregate $this->config = $config; } + /** + * + * @param ExternalUser $externalUser + * @throws \InvalidArgumentException + * @return ResourceOwnerConfig + */ + public function getOwnerFromExternalUser(ExternalUser $externalUser) + { + $provider = $externalUser->getProvider(); + if (strpos($provider, 'oauth_') !== 0) + throw new \InvalidArgumentException(sprintf('External user provider should start with "oauth_", got "%s"', $provider)); + + $name = substr($provider, strlen('oauth_')); + return $this->offsetGet($name); + } + public function offsetExists($offset) { return isset($this->config[$offset]); @@ -36,7 +54,9 @@ class ResourceOwnerMap implements \ArrayAccess, \IteratorAggregate public function offsetGet($offset) { - if(!$this->config[$offset] instanceof ResourceOwnerConfig) + if (!$this->offsetExists($offset)) + throw new \OutOfBoundsException(sprintf('Resource owner "%s" does not exist.', $offset)); + if (!$this->config[$offset] instanceof ResourceOwnerConfig) $this->config[$offset] = new ResourceOwnerConfig($this->config[$offset]); return $this->config[$offset]; } diff --git a/Resources/config/services.xml b/Resources/config/services.xml index ef51dfb..caddaab 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -30,10 +30,21 @@ + + + + - + + + + + + + +