You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
tos/EventListener/TosListener.php

200 lines
6.1 KiB

<?php
namespace vierbergenlars\AuthserverTosBundle\EventListener;
use Admin\AdminEvents;
use Admin\Event\FilterListEvent;
use App\Entity\User;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
use Registration\RegistrationEvents;
use Registration\Event\RegistrationFormEvent;
use Registration\Event\RegistrationHandleEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use vierbergenlars\AuthserverTosBundle\Entity\UserTos;
use vierbergenlars\AuthserverTosBundle\Form\AcceptTosType;
use Psr\Cache\CacheItemPoolInterface;
class TosListener implements EventSubscriberInterface
{
private $terms;
private $tosVersion;
/**
*
* @var EntityManagerInterface
*/
private $em;
/**
* @var CacheItemPoolInterface
*/
private $cache;
/**
*
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
*
* @var UrlGeneratorInterface
*/
private $urlGenerator;
public static function getSubscribedEvents()
{
$handlers = [
RegistrationEvents::BUILD_FORM => [
'onBuildForm',
-200
],
RegistrationEvents::HANDLE_FORM => [
'onHandleForm',
-20 // After persisting user
],
KernelEvents::REQUEST => 'onKernelRequest'
];
if (class_exists(AdminEvents::class) && defined(AdminEvents::class . '::FILTER_LIST')) {
$handlers[AdminEvents::FILTER_LIST] = [
[
'onFilterListCreateForm',
10
],
[
'onFilterListFilter',
-10
]
];
}
return $handlers;
}
public function __construct($terms, $tosVersion, CacheItemPoolInterface $cache, EntityManagerInterface $em, TokenStorageInterface $tokenStorage, UrlGeneratorInterface $urlGenerator)
{
$this->terms = $terms;
$this->tosVersion = $tosVersion;
$this->cache = $cache;
$this->em = $em;
$this->tokenStorage = $tokenStorage;
$this->urlGenerator = $urlGenerator;
}
public function onBuildForm(RegistrationFormEvent $event)
{
$event->getFormBuilder()->add('vl_tos', AcceptTosType::class, [
'terms' => $this->terms,
'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);
$this->cache->deleteItem('version_u_'.$user->getId());
}
}
private function getAcceptedTosVersion(User $user, $force = false) {
$cacheItem = $this->cache->getItem('version_u_'.$user->getId());
if(!$cacheItem->isHit() || $force) {
$userTos = $this->em->find(UserTos::class, $user);
if($userTos) {
$cacheItem->set($userTos->getAcceptedVersion());
} else {
$cacheItem->set(null);
}
$cacheItem->expiresAfter(24*60*60); // One day
$this->cache->saveDeferred($cacheItem);
}
return $cacheItem->get();
}
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;
if ($this->getAcceptedTosVersion($user) >= $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);
}
}
public function onFilterListCreateForm(FilterListEvent $event)
{
$event->getSearchFormBuilder()->add('tos_accepted', ChoiceType::class, [
'choices' => [
'Yes' => '1',
'No' => '0'
],
'expanded' => true,
'required' => false
]);
}
public function onFilterListFilter(FilterListEvent $event)
{
$field = $event->getSearchForm()->get('tos_accepted');
if ($field->isEmpty() || $field->getData() === null)
return;
$toses = $this->em->createQueryBuilder()
->select('t')
->from(UserTos::class, 't')
->where('t.acceptedVersion >= :currentVersion')
->setParameter('currentVersion', $this->tosVersion)
->groupBy('t.user')
->getQuery()
->getArrayResult();
$userIds = array_map(function ($tos) {
return $tos['user_id'];
}, $toses);
$expr = $field->getData() === "1" ? Criteria::expr()->in('id', $userIds) : Criteria::expr()->notIn('id', $userIds);
$event->addFilter('tos_accepted', $expr);
}
}