Initial commit

master
Lars Vierbergen 7 years ago
commit ac227fd8f7
  1. 1
      .gitignore
  2. 67
      AuthserverOAuthAccountBundle.php
  3. 63
      Controller/ConnectController.php
  4. 92
      DependencyInjection/AuthserverOAuthAccountExtension.php
  5. 78
      DependencyInjection/Configuration.php
  6. 66
      EventListener/LoginButtonListener.php
  7. 100
      EventListener/ProfileTemplateListener.php
  8. 76
      ResourceOwner/ResourceOwnerConfig.php
  9. 58
      ResourceOwner/ResourceOwnerMap.php
  10. 77
      ResourceOwner/ResourceOwnerMapIterator.php
  11. 16
      Resources/config/routing.yml
  12. 51
      Resources/config/services.xml
  13. 32
      Resources/views/Connect/connect_confirm.html.twig
  14. 5
      Resources/views/Connect/connect_success.html.twig
  15. 27
      Resources/views/Connect/disconnect_service.html.twig
  16. 24
      Resources/views/Profile/oauth_account.html.twig
  17. 2
      Resources/views/Profile/oauth_accounts_bottom.html.twig
  18. 5
      Resources/views/Profile/oauth_accounts_top.html.twig
  19. 60
      Routing/RouteProvider.php
  20. 124
      Security/Core/User/OAuthUserProvider.php
  21. 18
      composer.json
  22. 2266
      composer.lock

1
.gitignore vendored

@ -0,0 +1 @@
/vendor/

@ -0,0 +1,67 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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';
}
}

@ -0,0 +1,63 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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(),
]);
}
}

@ -0,0 +1,92 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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';
}
}

@ -0,0 +1,78 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

@ -0,0 +1,66 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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),
]);
}
}
}

@ -0,0 +1,100 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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'));
}
}

@ -0,0 +1,76 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

@ -0,0 +1,58 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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));
}
}

@ -0,0 +1,77 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

@ -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

@ -0,0 +1,51 @@
<?xml version="1.0" ?>
<!--
~ Authserver, an OAuth2-based single-signon authentication provider written in PHP.
~
~ Copyright (C) $today.date Lars Vierbergen
~
~ his program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="vierbergenlars.authserver_oauth_account.user_provider" class="vierbergenlars\AuthserverOAuthAccountBundle\Security\Core\User\OAuthUserProvider">
<argument type="service" id="doctrine" />
</service>
<service id="vierbergenlars.authserver_oauth_account.route_provider" class="vierbergenlars\AuthserverOAuthAccountBundle\Routing\RouteProvider">
<argument type="service" id="hwi_oauth.resource_ownermap.public" />
</service>
<service id="vierbergenlars.authserver_oauth_account.resource_owner_map" class="vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerMap">
<argument />
</service>
<!-- Event listeners -->
<service class="vierbergenlars\AuthserverOAuthAccountBundle\EventListener\LoginButtonListener">
<argument type="service" id="hwi_oauth.security.oauth_utils" />
<argument type="service" id="vierbergenlars.authserver_oauth_account.resource_owner_map" />
<tag name="kernel.event_subscriber" />
</service>
<service class="vierbergenlars\AuthserverOAuthAccountBundle\EventListener\ProfileTemplateListener">
<argument type="service" id="doctrine" />
<argument type="service" id="vierbergenlars.authserver_oauth_account.resource_owner_map" />
<argument type="service" id="hwi_oauth.security.oauth_utils"/>
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>

@ -0,0 +1,32 @@
{% extends '::base.html.twig' %}
{% block title %}{{ parent() }} - Connect Account{% endblock %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">{{ 'header.connecting' | trans({}, 'HWIOAuthBundle')}}</h3>
</div>
<div class="panel-body">
<p>{{ 'connect.confirm.text' | trans({'%service%': resourceOwnerConfig[service].serviceName | trans({}, 'HWIOAuthBundle'), '%name%': userInformation.realName}, 'HWIOAuthBundle') }}</p>
<p>
{{ form_start(form, {'action': path('hwi_oauth_connect_service', {'service': service, 'key': key})}) }}
{{ form_widget(form) }}
<div>
<button type="submit" class="btn btn-primary">{{ 'connect.confirm.submit' | trans({}, 'HWIOAuthBundle') }}</button>
<a href="{{ path('user_profile') }}" class="btn btn-link">{{ 'connect.confirm.cancel' | trans({}, 'HWIOAuthBundle') }}</a>
</div>
{{ form_end(form) }}
</p>
</div>
{% if userInformation.profilePicture is defined and userInformation.profilePicture is not empty %}
<div class="panel-body">
<img src="{{ userInformation.profilePicture }}" style="width: 100%" />
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,5 @@
{% extends 'HWIOAuthBundle::layout.html.twig' %}
{% block hwi_oauth_content %}
<h3>{{ 'header.success' | trans({'%name%': userInformation.realName}, 'HWIOAuthBundle') }}</h3>
{% endblock hwi_oauth_content %}

@ -0,0 +1,27 @@
{% extends '::base.html.twig' %}
{% block title %}{{ parent() }} - Disconnect Account{% endblock %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Disconnecting</h3>
</div>
<div class="panel-body">
<p>Are you sure you want to disconnect your {{ resourceOwnerConfig[externalUser.provider].serviceName }} account "{{ externalUser.providerFriendlyName }}"?</p>
<p>
{{ form_start(form) }}
{{ form_widget(form) }}
<div>
<button type="submit" class="btn btn-primary">Disconnect account</button>
<a href="{{ path('user_profile') }}" class="btn btn-link">Cancel</a>
</div>
{{ form_end(form) }}
</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,24 @@
{# @var resourceOwnerConfig \vierbergenlars\AuthserverOAuthAccountBundle\ResourceOwner\ResourceOwnerConfig #}
{# @var externalUsers \vierbergenlars\AuthserverExternalAccountBundle\Entity\ExternalUser[] #}
<h4>
{% if resourceOwnerConfig.icon %}
{{ icon(resourceOwnerConfig.icon) }}
{% endif %}
{{ resourceOwnerConfig.serviceName }}
</h4>
{% for externalUser in externalUsers %}
Connected to {{ externalUser.providerFriendlyName }}
<a href="{{ url('vierbergenlars_oauth_account_disconnect', {externalUser: externalUser.id}) }}" class="btn btn-link btn-sm">{{ icon('chain-broken') }} Disconnect account</a>
<br>
{% endfor %}
{% set buttonConfig = resourceOwnerConfig.connectButton %}
<a href="{{ hwi_oauth_login_url(resourceOwner) }}"
class="btn btn-{{ buttonConfig.style }}">
{% if buttonConfig.icon %}
{{ icon(buttonConfig.icon) }}
{% endif %}
{{ buttonConfig.label }}
</a>

@ -0,0 +1,5 @@
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">External authentication sources</h2>
</div>
<div class="panel-body">

@ -0,0 +1,60 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* 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;
}
}

@ -0,0 +1,124 @@
<?php
/**
* Authserver, an OAuth2-based single-signon authentication provider written in PHP.
*
* Copyright (C) $today.date Lars Vierbergen
*
* his program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

@ -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"
}
]
}

2266
composer.lock generated

File diff suppressed because it is too large Load Diff