<?php
namespace Cms\TenantBundle\Listener;
use Cms\CoreBundle\Service\ContextManager;
use Cms\TenantBundle\Controller\Dashboard\PolicyController;
use Cms\TenantBundle\Entity\TenantTypeEmbeddable;
use DateTimeImmutable;
use Platform\SecurityBundle\Controller\LoginController;
use Platform\SecurityBundle\Controller\SingleSignOnController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouterInterface;
final class PolicyListener implements EventSubscriberInterface
{
// DI
protected ContextManager $contextManager;
protected RouterInterface $router;
protected FileLocator $locator;
protected ParameterBagInterface $params;
/**
* @param ContextManager $contextManager
* @param RouterInterface $router
* @param FileLocator $locator
* @param ParameterBagInterface $params
*/
public function __construct(
ContextManager $contextManager,
RouterInterface $router,
FileLocator $locator,
ParameterBagInterface $params
)
{
$this->contextManager = $contextManager;
$this->router = $router;
$this->locator = $locator;
$this->params = $params;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest'],
];
}
/**
* @param RequestEvent $event
*/
public function onKernelRequest(RequestEvent $event): void
{
static $skipRoutes = [
PolicyController::ROUTES__VIEW,
PolicyController::ROUTES__ACCEPT,
PolicyController::ROUTES__TERMS,
PolicyController::ROUTES__TERMS_ACCEPT,
LoginController::ROUTES__SELECT,
SingleSignOnController::ROUTES__START,
SingleSignOnController::ROUTES__FINISH,
'app.platform.security.logout',
];
// see if we are working with the main request
$request = $event->getRequest();
if ( ! $event->isMainRequest()) {
return;
}
// there are certain urls we don't want covered by the policy
if (in_array($request->get('_route'), $skipRoutes, true)) {
return;
}
// obtain the tenant
$tenant = $this->contextManager->getGlobalContext()->getTenant();
if ( ! $tenant) {
return;
}
// if we are currently impersonating a user, we don't want this person to accept the policy on their behalf.
// in this case we want to skip if impersonation is active, otherwise, pull the user
if ($this->contextManager->getGlobalContext()->isImpersonating()) {
return;
}
$currentUser = $this->contextManager->getGlobalContext()->getAuthenticatedAccount();
if ( ! $currentUser) {
return;
}
if ( ! $request->isMethod(Request::METHOD_GET)) {
return;
}
if ($request->isXmlHttpRequest()) {
return;
}
// check our company policy first
$timestamp = DateTimeImmutable::createFromFormat(
DATE_ISO8601,
$this->params->get('cms.core.policies.ferpa.timestamp')
);
// go ahead and check if the timestamps match or has not been accepted yet
// redirect if this person needs to accept our policy
if ($tenant->getType()->getPrimary() !== TenantTypeEmbeddable::PRIMARY__CDD) {
if ( ! $currentUser->getTermsAcceptedOn() || $currentUser->getTermsAcceptedOn() < $timestamp) {
$event->setResponse(
new RedirectResponse($this->router->generate(PolicyController::ROUTES__TERMS))
);
return;
}
}
// if the tenant doesn't have a policy, nothing to do
if ( ! $tenant->getPolicy()) {
return;
}
// if there is a policy, and it has not yet been accepted or is out-of-date, redirect to review it and accept
if ( ! $currentUser->getPolicyAcceptedOn() || $currentUser->getPolicyAcceptedOn() < $tenant->getPolicy()) {
$event->setResponse(
new RedirectResponse($this->router->generate(PolicyController::ROUTES__VIEW))
);
}
}
}