Browse Source

logout solidified

main
astanley 8 months ago
parent
commit
84dc14bc99
  1. 71
      src/EventSubscriber/RedirectSubscriber.php
  2. 2
      url_permission_redirect.services.yml

71
src/EventSubscriber/RedirectSubscriber.php

@ -15,8 +15,14 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
/** /**
* Subscribes to kernel request events to redirect users based on permissions. * Subscribes to kernel request events to redirect users based on permissions.
* *
* If a user has the 'access protected domain' permission and is not already * - Authenticated users with 'access protected domain' permission are
* accessing the site via the protected domain, they will be redirected there. * redirected to the protected domain when accessing admin paths.
* - Anonymous users logging in from the public domain log in normally, but if
* they gain access permission, they're redirected to the protected domain to
* log in again.
* - A message is shown on the protected domain when redirected, and query
* strings are cleaned.
* - A logout finalizer allows users to fully log out across both domains.
*/ */
class RedirectSubscriber implements EventSubscriberInterface { class RedirectSubscriber implements EventSubscriberInterface {
@ -37,19 +43,19 @@ class RedirectSubscriber implements EventSubscriberInterface {
/** /**
* The Messenger service. * The Messenger service.
* *
* @var \Drupal\Core\Messenger\ * @var \Drupal\Core\Messenger\MessengerInterface
*/ */
protected $messenger; protected $messenger;
/** /**
* Constructs a new RedirectSubscriber. * Constructs a new RedirectSubscriber object.
* *
* @param \Drupal\Core\Session\AccountProxyInterface $current_user * @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user. * The current user.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory. * The configuration factory.
* @param Drupal\Core\Messenger\MessengerInterface $messenger * @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger. * The messenger service.
*/ */
public function __construct(AccountProxyInterface $current_user, ConfigFactoryInterface $config_factory, MessengerInterface $messenger) { public function __construct(AccountProxyInterface $current_user, ConfigFactoryInterface $config_factory, MessengerInterface $messenger) {
$this->currentUser = $current_user; $this->currentUser = $current_user;
@ -58,41 +64,44 @@ class RedirectSubscriber implements EventSubscriberInterface {
} }
/** /**
* {@inheritdoc} * Handles kernel request events.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* The event to process.
*/ */
public function onRequest(RequestEvent $event) { public function onRequest(RequestEvent $event) {
$request = $event->getRequest(); $request = $event->getRequest();
$host = $request->getHost(); $host = $request->getHost();
$uri = $request->getRequestUri(); $uri = $request->getRequestUri();
$config = $this->configFactory->get('url_permission_redirect.settings'); $config = $this->configFactory->get('url_permission_redirect.settings');
$protectedDomain = $config->get('protected_domain') ?? FALSE; $protectedDomain = $this->sanitizeDomain($config->get('protected_domain') ?? '');
// Redirect logged-in users with access permission to protected domain. // Redirect logged-in users with access permission to protected domain for admin routes only.
if ($protectedDomain && $this->currentUser->isAuthenticated() && if ($protectedDomain && $this->currentUser->isAuthenticated() &&
$this->currentUser->hasPermission('access protected domain')) { $this->currentUser->hasPermission('access protected domain')) {
if ($host !== $protectedDomain) { if ($host !== $protectedDomain && str_starts_with($uri, '/admin')) {
$redirect_url = 'https://' . $protectedDomain . $uri; $redirect_url = 'https://' . $protectedDomain . $uri;
$event->setResponse(new TrustedRedirectResponse($redirect_url, 302)); $event->setResponse(new TrustedRedirectResponse($redirect_url, 302));
return; return;
} }
} }
// Redirect anonymous users attempting to log in from public domain. // Let anonymous users log in normally.
if ($host !== $protectedDomain && $uri === '/user/login' && $this->currentUser->isAnonymous()) { if ($uri === '/user/login' && $this->currentUser->isAnonymous()) {
$destination = $request->query->get('destination'); return;
$redirect_url = 'https://' . $protectedDomain . '/user?redirect_message=1'; }
if ($destination) {
$redirect_url .= '&destination=' . urlencode($destination);
}
// Redirect users who now have permission and are still on public site.
if ($host !== $protectedDomain && $uri === '/user' && $this->currentUser->isAuthenticated() &&
$this->currentUser->hasPermission('access protected domain')) {
$redirect_url = 'https://' . $protectedDomain . '/user?redirect_message=1';
$event->setResponse(new TrustedRedirectResponse($redirect_url, 302)); $event->setResponse(new TrustedRedirectResponse($redirect_url, 302));
return; return;
} }
// Show redirect message on target domain if query parameter is present. // Show redirect message on target domain if query parameter is present.
if ($host === $protectedDomain && $request->query->get('redirect_message') === '1') { if ($host === $protectedDomain && $request->query->get('redirect_message') === '1') {
$this->messenger->addStatus('You were redirected here to log in securely.'); $this->messenger->addStatus('You were redirected here to log in securely. If already logged in, no action is needed.');
// Clean the query string by removing redirect_message and reloading. // Clean the query string by removing redirect_message and reloading.
$query = $request->query->all(); $query = $request->query->all();
@ -102,6 +111,28 @@ class RedirectSubscriber implements EventSubscriberInterface {
$clean_url = Url::fromUri('internal:' . $current_path, ['query' => $query])->toString(); $clean_url = Url::fromUri('internal:' . $current_path, ['query' => $query])->toString();
$event->setResponse(new RedirectResponse($clean_url, 302)); $event->setResponse(new RedirectResponse($clean_url, 302));
} }
// Final logout handling: if coming from the protected domain, force logout here too.
if ($uri === '/user/logout' && $request->query->get('final') === '1') {
if ($this->currentUser->isAuthenticated()) {
user_logout();
}
$url = Url::fromRoute('user.login')->toString();
$event->setResponse(new RedirectResponse($url));
}
}
/**
* Strips protocol and trailing slashes from a domain.
*
* @param string $domain
* The domain string to sanitize.
*
* @return string
* The sanitized domain.
*/
protected function sanitizeDomain(string $domain): string {
return preg_replace('#^https?://#', '', rtrim($domain, '/'));
} }
/** /**

2
url_permission_redirect.services.yml

@ -1,6 +1,6 @@
services: services:
url_permission_redirect.event_subscriber: url_permission_redirect.event_subscriber:
class: Drupal\url_permission_redirect\EventSubscriber\RedirectSubscriber class: Drupal\url_permission_redirect\EventSubscriber\RedirectSubscriber
arguments: ['@current_user', '@config.factory, '@messenger'] arguments: ['@current_user', '@config.factory', '@messenger']
tags: tags:
- { name: event_subscriber } - { name: event_subscriber }

Loading…
Cancel
Save