diff --git a/DependencyInjection/AuthserverExternalAccountExtension.php b/DependencyInjection/AuthserverExternalAccountExtension.php
index 12591cf..107c906 100644
--- a/DependencyInjection/AuthserverExternalAccountExtension.php
+++ b/DependencyInjection/AuthserverExternalAccountExtension.php
@@ -24,13 +24,14 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
+use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
-class AuthserverExternalAccountExtension extends Extension
+class AuthserverExternalAccountExtension extends Extension implements PrependExtensionInterface
{
/**
* {@inheritDoc}
@@ -42,4 +43,15 @@ class AuthserverExternalAccountExtension extends Extension
$loader = new Loader\XmlFileLoader($container, $fileLocator);
$loader->load('services.xml');
}
+
+ public function prepend(ContainerBuilder $container)
+ {
+ $container->prependExtensionConfig('security', [
+ 'providers' => [
+ 'temporary_user' => [
+ 'id' => 'vierbergenlars.authserver_external_account.temporary_user_provider'
+ ]
+ ]
+ ]);
+ }
}
diff --git a/Entity/TemporaryUser.php b/Entity/TemporaryUser.php
new file mode 100644
index 0000000..70dc086
--- /dev/null
+++ b/Entity/TemporaryUser.php
@@ -0,0 +1,52 @@
+.
+ */
+namespace vierbergenlars\AuthserverExternalAccountBundle\Entity;
+
+use Registration\Entity\TemporaryUser as BaseTemporaryUser;
+
+class TemporaryUser extends BaseTemporaryUser
+{
+
+ /**
+ *
+ * @var ExternalUser
+ */
+ private $externalUser;
+
+ public function getExternalUser()
+ {
+ return $this->externalUser;
+ }
+
+ public function setExternalUser(ExternalUser $externalUser)
+ {
+ $this->externalUser = $externalUser;
+ }
+
+ public function getUsername()
+ {
+ return '$' . $this->externalUser->getProvider() . '$' . $this->externalUser->getProviderRef() . '$';
+ }
+
+ public function getDisplayName()
+ {
+ return $this->externalUser->getProviderFriendlyName();
+ }
+}
diff --git a/EventListener/RegistrationHandlerListener.php b/EventListener/RegistrationHandlerListener.php
new file mode 100644
index 0000000..f582755
--- /dev/null
+++ b/EventListener/RegistrationHandlerListener.php
@@ -0,0 +1,123 @@
+.
+ */
+namespace vierbergenlars\AuthserverExternalAccountBundle\EventListener;
+
+use Registration\Event\RegistrationHandleEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Registration\RegistrationEvents;
+use Registration\Event\RegistrationFormEvent;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
+use vierbergenlars\AuthserverExternalAccountBundle\Entity\TemporaryUser;
+use Doctrine\ORM\EntityManagerInterface;
+
+class RegistrationHandlerListener implements EventSubscriberInterface
+{
+
+ /**
+ *
+ * @var TokenStorageInterface
+ */
+ private $tokenStorage;
+
+ /**
+ *
+ * @var EntityManagerInterface
+ */
+ private $em;
+
+ public static function getSubscribedEvents()
+ {
+ return [
+ RegistrationEvents::BUILD_FORM => [
+ 'onBuildForm',
+ 10
+ ],
+ RegistrationEvents::HANDLE_FORM => [
+ [
+ 'onHandleFormSetPasswordEnabled',
+ 10
+ ],
+ [
+ 'onHandleFormConnectExternal',
+ 0
+ ],
+ [
+ 'onHandleFormLogoutExternal',
+ -250
+ ]
+ ]
+ ];
+ }
+
+ public function __construct(EntityManagerInterface $em, TokenStorageInterface $tokenStorage)
+ {
+ $this->em = $em;
+ $this->tokenStorage = $tokenStorage;
+ }
+
+ private function getTemporaryUser()
+ {
+ $token = $this->tokenStorage->getToken();
+ if (!$token)
+ return null;
+ $user = $token->getUser();
+ if ($user instanceof TemporaryUser)
+ return $user;
+ return null;
+ }
+
+ public function onBuildForm(RegistrationFormEvent $event)
+ {
+ if ($user = $this->getTemporaryUser()) {
+ $event->getFormBuilder()->remove('password');
+ $event->getFormBuilder()
+ ->getData()
+ ->setDisplayName($user->getDisplayName())
+ ->setPasswordEnabled(2);
+ }
+ }
+
+ public function onHandleFormSetPasswordEnabled(RegistrationHandleEvent $event)
+ {
+ $user = $event->getForm()->getData();
+ if (!$user)
+ return;
+ /* @var $user User */
+ $user->setPasswordEnabled(2);
+ }
+
+ public function onHandleFormConnectExternal(RegistrationHandleEvent $event)
+ {
+ $user = $event->getForm()->getData();
+ if (!$user)
+ return;
+ /* @var $user User */
+ if ($temporaryUser = $this->getTemporaryUser()) {
+ $temporaryUser->getExternalUser()->setUser($user);
+ $this->em->persist($temporaryUser->getExternalUser());
+ }
+ }
+
+ public function onHandleFormLogoutExternal(RegistrationHandleEvent $event)
+ {
+ if ($this->getTemporaryUser())
+ $this->tokenStorage->setToken(null);
+ }
+}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 1c2d329..e75eb6a 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -32,6 +32,12 @@
+
+
+
+
+
+
diff --git a/Resources/migrations/VersionAuthserverExternalAccount20171020080018.php b/Resources/migrations/VersionAuthserverExternalAccount20171020080018.php
new file mode 100644
index 0000000..55a82b3
--- /dev/null
+++ b/Resources/migrations/VersionAuthserverExternalAccount20171020080018.php
@@ -0,0 +1,47 @@
+.
+ */
+namespace Application\Migrations;
+
+use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Schema\Schema;
+
+class VersionAuthserverExternalAccount20171020080018 extends AbstractMigration
+{
+
+ public function up(Schema $schema)
+ {
+ $externalUser = $schema->getTable('vierbergenlars_external_account_external_user');
+
+ foreach ($externalUser->getForeignKeys() as $fk)
+ $externalUser->removeForeignKey($fk->getName());
+
+ $externalUser->addForeignKeyConstraint('auth_users', [
+ 'user_id'
+ ], [
+ 'id'
+ ], [
+ 'onDelete' => 'CASCADE'
+ ], 'fk_vl_ea_external_user');
+ }
+
+ public function down(Schema $schema)
+ {}
+}
+
diff --git a/Resources/migrations/VersionAuthserverExternalAccount20171020220239.php b/Resources/migrations/VersionAuthserverExternalAccount20171020220239.php
new file mode 100644
index 0000000..2d7afe9
--- /dev/null
+++ b/Resources/migrations/VersionAuthserverExternalAccount20171020220239.php
@@ -0,0 +1,43 @@
+.
+ */
+namespace Application\Migrations;
+
+use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Schema\Schema;
+
+class VersionAuthserverExternalAccount20171020220239 extends AbstractMigration
+{
+
+ public function up(Schema $schema)
+ {
+ $condition = 'WHERE ea1.id > ea2.id AND ea1.provider = ea2.provider AND ea1.provider_ref = ea2.provider_ref';
+ $duplicates = $this->connection->fetchAll('SELECT DISTINCT ea1.id AS id FROM vierbergenlars_external_account_external_user AS ea1 JOIN vierbergenlars_external_account_external_user ea2 ' . $condition);
+ $duplicateIds = array_map(function ($row) {
+ return $row['id'];
+ }, $duplicates);
+ $this->connection->executeUpdate('DELETE FROM vierbergenlars_external_account_external_user WHERE id IN(' . implode(',', $duplicateIds) . ')');
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->throwIrreversibleMigrationException('Duplicate external account references have been cleared.');
+ }
+}
+
diff --git a/Resources/migrations/VersionAuthserverExternalAccount20171020220751.php b/Resources/migrations/VersionAuthserverExternalAccount20171020220751.php
new file mode 100644
index 0000000..4ce2560
--- /dev/null
+++ b/Resources/migrations/VersionAuthserverExternalAccount20171020220751.php
@@ -0,0 +1,45 @@
+.
+ */
+namespace Application\Migrations;
+
+use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Schema\Schema;
+
+class VersionAuthserverExternalAccount20171020220751 extends AbstractMigration
+{
+
+ public function up(Schema $schema)
+ {
+ $externalUser = $schema->getTable('vierbergenlars_external_account_external_user');
+
+ if ($externalUser->hasIndex('uniq_vl_ea_external_user'))
+ $externalUser->dropIndex('uniq_vl_ea_external_user');
+ $externalUser->addUniqueIndex([
+ 'provider',
+ 'provider_ref'
+ ], 'uniq_vl_ea_external_user');
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->throwIrreversibleMigrationException('Duplicate values on (provider, provider_ref) are no longer allowed.');
+ }
+}
+
diff --git a/Security/Core/User/TemporaryUserProvider.php b/Security/Core/User/TemporaryUserProvider.php
new file mode 100644
index 0000000..e22328d
--- /dev/null
+++ b/Security/Core/User/TemporaryUserProvider.php
@@ -0,0 +1,48 @@
+.
+ */
+namespace vierbergenlars\AuthserverExternalAccountBundle\Security\Core\User;
+
+use Symfony\Component\Security\Core\User\UserProviderInterface;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
+use vierbergenlars\AuthserverExternalAccountBundle\Entity\TemporaryUser;
+use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
+
+class TemporaryUserProvider implements UserProviderInterface
+{
+
+ public function supportsClass($class)
+ {
+ return $class === TemporaryUser::class;
+ }
+
+ public function refreshUser(UserInterface $user)
+ {
+ if (!$this->supportsClass(get_class($user)))
+ throw new UnsupportedUserException(sprintf('Expected instance of %s, got instance of %s', TemporaryUser::class, get_class($user)));
+ return $user;
+ }
+
+ public function loadUserByUsername($username)
+ {
+ throw new UsernameNotFoundException();
+ }
+}
+