Move tos form to a separate form type, add a request interceptor to redirect to accept TOS when they have not accepted the latest version

master
Lars Vierbergen 7 years ago
parent d1ea33b0bf
commit 2640823988
  1. 55
      Controller/TosController.php
  2. 75
      EventListener/RegistrationListener.php
  3. 122
      EventListener/TosListener.php
  4. 39
      Form/AcceptTosType.php
  5. 4
      Resources/config/routing.yml
  6. 4
      Resources/config/services.xml
  7. 16
      Resources/views/Tos/accept.html.twig

@ -0,0 +1,55 @@
<?php
namespace vierbergenlars\AuthserverTosBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\View;
use vierbergenlars\AuthserverTosBundle\Entity\UserTos;
use vierbergenlars\AuthserverTosBundle\Form\AcceptTosType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Braincrafted\Bundle\BootstrapBundle\Form\Type\FormActionsType;
class TosController extends Controller
{
/**
* @View()
*/
public function acceptAction(Request $request)
{
$user = $this->getUser();
$em = $this->getDoctrine()->getManagerForClass(UserTos::class);
$userTos = $em->find(UserTos::class, $user);
if (!$userTos) {
$userTos = new UserTos($user);
$em->persist($userTos);
}
$tosUrl = $this->container->getParameter('vierbergenlars_tos.tos_url');
$formBuilder = $this->createFormBuilder();
$formBuilder->add('vl_tos', AcceptTosType::class, [
'url' => $tosUrl
]);
$formBuilder->add('submit', SubmitType::class, [
'label' => 'Submit'
]);
$form = $formBuilder->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$userTos->setAcceptedVersion($this->container->getParameter('vierbergenlars_tos.tos_version'));
$em->flush();
return $this->redirectToRoute('user_profile');
}
return [
'form' => $form,
'tos_url' => $tosUrl
];
}
}

@ -1,75 +0,0 @@
<?php
namespace vierbergenlars\AuthserverTosBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Registration\RegistrationEvents;
use Registration\Event\RegistrationFormEvent;
use Registration\Event\RegistrationHandleEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Validator\Constraints\IsTrue;
use vierbergenlars\AuthserverTosBundle\Entity\UserTos;
use Doctrine\ORM\EntityManagerInterface;
class RegistrationListener implements EventSubscriberInterface
{
private $tosUrl;
private $tosVersion;
/**
*
* @var EntityManagerInterface
*/
private $em;
public static function getSubscribedEvents()
{
return [
RegistrationEvents::BUILD_FORM => [
'onBuildForm',
-200
],
RegistrationEvents::HANDLE_FORM => [
'onHandleForm',
-20 // After persisting user
]
];
}
public function __construct($tosUrl, $tosVersion, EntityManagerInterface $em)
{
$this->tosUrl = $tosUrl;
$this->tosVersion = $tosVersion;
$this->em = $em;
}
public function onBuildForm(RegistrationFormEvent $event)
{
$event->getFormBuilder()->add('vl_tos_accept', CheckboxType::class, [
'label' => 'I accept the <a href="' . htmlentities($this->tosUrl, ENT_HTML5 | ENT_QUOTES) . '">terms of service</a>',
'mapped' => false,
'attr' => [
'align_with_widget' => true
],
'constraints' => [
new IsTrue([
'message' => 'You must accept the terms of service.'
])
]
]);
}
public function onHandleForm(RegistrationHandleEvent $event)
{
if ($event->getForm()
->get('vl_tos_accept')
->getData()) {
$user = $event->getForm()->getData();
/* @var $user \App\Entity\User */
$tosUser = new UserTos($user);
$tosUser->setAcceptedVersion($this->tosVersion);
$this->em->persist($tosUser);
}
}
}

@ -0,0 +1,122 @@
<?php
namespace vierbergenlars\AuthserverTosBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Registration\RegistrationEvents;
use Registration\Event\RegistrationFormEvent;
use Registration\Event\RegistrationHandleEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Validator\Constraints\IsTrue;
use vierbergenlars\AuthserverTosBundle\Entity\UserTos;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use vierbergenlars\AuthserverTosBundle\Form\AcceptTosType;
class TosListener implements EventSubscriberInterface
{
private $tosUrl;
private $tosVersion;
/**
*
* @var EntityManagerInterface
*/
private $em;
/**
*
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
*
* @var UrlGeneratorInterface
*/
private $urlGenerator;
public static function getSubscribedEvents()
{
return [
RegistrationEvents::BUILD_FORM => [
'onBuildForm',
-200
],
RegistrationEvents::HANDLE_FORM => [
'onHandleForm',
-20 // After persisting user
],
KernelEvents::REQUEST => 'onKernelRequest'
];
}
public function __construct($tosUrl, $tosVersion, EntityManagerInterface $em, TokenStorageInterface $tokenStorage, UrlGeneratorInterface $urlGenerator)
{
$this->tosUrl = $tosUrl;
$this->tosVersion = $tosVersion;
$this->em = $em;
$this->tokenStorage = $tokenStorage;
$this->urlGenerator = $urlGenerator;
}
public function onBuildForm(RegistrationFormEvent $event)
{
$event->getFormBuilder()->add('vl_tos', AcceptTosType::class, [
'url' => $this->tosUrl,
'mapped' => false
]);
}
public function onHandleForm(RegistrationHandleEvent $event)
{
if ($event->getForm()
->get('vl_tos')
->getData()['accept']) {
$user = $event->getForm()->getData();
/* @var $user \App\Entity\User */
$tosUser = new UserTos($user);
$tosUser->setAcceptedVersion($this->tosVersion);
$this->em->persist($tosUser);
}
}
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest())
return;
if (!($token = $this->tokenStorage->getToken()))
return;
if (!($user = $token->getUser()))
return;
if (!($user instanceof User))
return;
if ($token->hasAttribute('vl_tos_accept_ok'))
return;
$userTos = $this->em->find(UserTos::class, $user);
if ($userTos && $userTos->getAcceptedVersion() >= $this->tosVersion) {
$token->setAttribute('vl_tos_accept_ok', true);
return;
}
if ($event->getRequest()->getRequestFormat() !== 'html') {
throw new AccessDeniedHttpException('You need to accept the latest version of the terms of service.');
}
switch ($event->getRequest()->attributes->get('_route')) {
case 'vl_tos_accept':
break;
default:
$response = RedirectResponse::create($this->urlGenerator->generate('vl_tos_accept'));
$event->setResponse($response);
}
}
}

@ -0,0 +1,39 @@
<?php
namespace vierbergenlars\AuthserverTosBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Validator\Constraints\IsTrue;
class AcceptTosType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('accept', CheckboxType::class, [
'label' => 'I accept the <a href="' . htmlentities($options['url'], ENT_HTML5 | ENT_QUOTES) . '">terms of service</a>',
'attr' => [
'align_with_widget' => true
],
'constraints' => [
new IsTrue([
'message' => 'You must accept the terms of service.'
])
]
]);
}
/**
*
* {@inheritdoc}
* @see \Symfony\Component\Form\AbstractType::configureOptions()
*/
public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver)
{
$resolver->setDefined('url')
->setRequired('url')
->addAllowedTypes('url', 'string')
->setDefault('label', false);
}
}

@ -0,0 +1,4 @@
vl_tos_accept:
path: /usr/tos/accept
defaults:
_controller: AuthserverTosBundle:Tos:accept

@ -19,10 +19,12 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services> <services>
<service class="vierbergenlars\AuthserverTosBundle\EventListener\RegistrationListener"> <service class="vierbergenlars\AuthserverTosBundle\EventListener\TosListener">
<argument>%vierbergenlars_tos.tos_url%</argument> <argument>%vierbergenlars_tos.tos_url%</argument>
<argument>%vierbergenlars_tos.tos_version%</argument> <argument>%vierbergenlars_tos.tos_version%</argument>
<argument type="service" id="doctrine.orm.entity_manager" /> <argument type="service" id="doctrine.orm.entity_manager" />
<argument type="service" id="security.token_storage" />
<argument type="service" id="router" />
<tag name="kernel.event_subscriber" /> <tag name="kernel.event_subscriber" />
</service> </service>
</services> </services>

@ -0,0 +1,16 @@
{% extends '::base.html.twig' %}
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-info">Our terms of service have been updated.<br>You must accept these terms to be able to continue using the application.</div>
</div>
<div class="col-xs-12">
<iframe src="{{ tos_url }}" style="width: 100%; height: 70vh;"></iframe>
</div>
<div class="col-xs-12">
{{ form(form, {'style': 'horizontal'}) }}
</div>
</div>
</div>
{% endblock %}