added autologout module
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\Core\Session\AnonymousUserSession;
|
||||
|
||||
/**
|
||||
* Defines an AutologoutManager service.
|
||||
*/
|
||||
class AutologoutManager implements AutologoutManagerInterface {
|
||||
|
||||
/**
|
||||
* The module manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The config object for 'autologout.settings'.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $autoLogoutSettings;
|
||||
|
||||
/**
|
||||
* The config factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutManager object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->autoLogoutSettings = $config_factory->get('autologout.settings');
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preventJs() {
|
||||
foreach ($this->moduleHandler->invokeAll('autologout_prevent') as $prevent) {
|
||||
if (!empty($prevent)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function refreshOnly() {
|
||||
foreach ($this->moduleHandler->invokeAll('autologout_refresh_only') as $module_refresh_only) {
|
||||
if (!empty($module_refresh_only)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function inactivityMessage() {
|
||||
$message = $this->autoLogoutSettings->get('inactivity_message');
|
||||
if (!empty($message)) {
|
||||
drupal_set_message($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function logout() {
|
||||
$user = \Drupal::currentUser();
|
||||
|
||||
if ($this->autoLogoutSettings->get('use_watchdog')) {
|
||||
\Drupal::logger('user')->info('Session automatically closed for %name by autologout.', ['%name' => $user->getAccountName()]);
|
||||
}
|
||||
|
||||
// Destroy the current session.
|
||||
$this->moduleHandler->invokeAll('user_logout', [$user]);
|
||||
\Drupal::service('session_manager')->destroy();
|
||||
$user->setAccount(new AnonymousUserSession());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoleTimeout() {
|
||||
$roles = user_roles(TRUE);
|
||||
$role_timeout = [];
|
||||
|
||||
// Go through roles, get timeouts for each and return as array.
|
||||
foreach ($roles as $name => $role) {
|
||||
$role_settings = $this->configFactory->get('autologout.role.' . $name);
|
||||
if ($role_settings->get('enabled')) {
|
||||
$timeout_role = $role_settings->get('timeout');
|
||||
$role_timeout[$name] = $timeout_role;
|
||||
}
|
||||
}
|
||||
return $role_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRemainingTime() {
|
||||
$timeout = $this->getUserTimeout();
|
||||
$time_passed = isset($_SESSION['autologout_last']) ? REQUEST_TIME - $_SESSION['autologout_last'] : 0;
|
||||
return $timeout - $time_passed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createTimer() {
|
||||
return $this->getRemainingTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUserTimeout($uid = NULL) {
|
||||
if (is_null($uid)) {
|
||||
// If $uid is not provided, use the logged in user.
|
||||
$user = \Drupal::currentUser();
|
||||
}
|
||||
else {
|
||||
$user = User::load($uid);
|
||||
}
|
||||
|
||||
if ($user->id() == 0) {
|
||||
// Anonymous doesn't get logged out.
|
||||
return 0;
|
||||
}
|
||||
$user_timeout = \Drupal::service('user.data')->get('autologout', $user->id(), 'timeout');
|
||||
|
||||
if (is_numeric($user_timeout)) {
|
||||
// User timeout takes precedence.
|
||||
return $user_timeout;
|
||||
}
|
||||
|
||||
// Get role timeouts for user.
|
||||
if ($this->autoLogoutSettings->get('role_logout')) {
|
||||
$user_roles = $user->getRoles();
|
||||
$output = [];
|
||||
$timeouts = $this->getRoleTimeout();
|
||||
foreach ($user_roles as $rid => $role) {
|
||||
if (isset($timeouts[$role])) {
|
||||
$output[$rid] = $timeouts[$role];
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the lowest timeout value to be session timeout value.
|
||||
if (!empty($output)) {
|
||||
// If one of the user's roles has a unique timeout, use this.
|
||||
return min($output);
|
||||
}
|
||||
}
|
||||
|
||||
// If no user or role override exists, return the default timeout.
|
||||
return $this->autoLogoutSettings->get('timeout');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function logoutRole($user) {
|
||||
if ($this->autoLogoutSettings->get('role_logout')) {
|
||||
foreach ($user->roles as $name => $role) {
|
||||
if ($this->configFactory->get('autologout.role.' . $name . '.enabled')) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout;
|
||||
|
||||
/**
|
||||
* Interface for AutologoutManager.
|
||||
*/
|
||||
interface AutologoutManagerInterface {
|
||||
|
||||
/**
|
||||
* Get the timer HTML markup.
|
||||
*
|
||||
* @return string
|
||||
* HTML to insert a countdown timer.
|
||||
*/
|
||||
public function createTimer();
|
||||
|
||||
/**
|
||||
* Get the time remaining before logout.
|
||||
*
|
||||
* @return int
|
||||
* Number of seconds remaining.
|
||||
*/
|
||||
public function getRemainingTime();
|
||||
|
||||
/**
|
||||
* Go through every role to get timeout value, default is the global timeout.
|
||||
*
|
||||
* @return int
|
||||
* Number of seconds timeout set for the user role.
|
||||
*/
|
||||
public function getRoleTimeout();
|
||||
|
||||
/**
|
||||
* Get a user's timeout in seconds.
|
||||
*
|
||||
* @param int $uid
|
||||
* (Optional) Provide a user's uid to get the timeout for.
|
||||
* Default is the logged in user.
|
||||
*
|
||||
* @return int
|
||||
* The number of seconds the user can be idle for before being logged out.
|
||||
* A value of 0 means no timeout.
|
||||
*/
|
||||
public function getUserTimeout($uid = NULL);
|
||||
|
||||
/**
|
||||
* Perform Logout.
|
||||
*
|
||||
* Helper to perform the actual logout. Destroys the session of the logged
|
||||
* in user.
|
||||
*/
|
||||
public function logout();
|
||||
|
||||
/**
|
||||
* Helper to determine if a given user should be autologged out.
|
||||
*/
|
||||
public function logoutRole($user);
|
||||
|
||||
/**
|
||||
* Display the inactivity message if required when the user is logged out.
|
||||
*/
|
||||
public function inactivityMessage();
|
||||
|
||||
/**
|
||||
* Determine if autologout should be prevented.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if there is a reason not to autologout
|
||||
* the current user on the current page.
|
||||
*/
|
||||
public function preventJs();
|
||||
|
||||
/**
|
||||
* Determine if connection should be refreshed.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if something about the current context should keep the connection
|
||||
* open. FALSE and the standard countdown to autologout applies.
|
||||
*/
|
||||
public function refreshOnly();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Controller;
|
||||
|
||||
use Drupal\autologout\AutologoutManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Ajax;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
|
||||
/**
|
||||
* Returns responses for autologout module routes.
|
||||
*/
|
||||
class AutologoutController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The autologout manager service.
|
||||
*
|
||||
* @var \Drupal\autologout\AutologoutManagerInterface
|
||||
*/
|
||||
protected $autoLogoutManager;
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutSubscriber object.
|
||||
*
|
||||
* @param \Drupal\autologout\AutologoutManagerInterface $autologout
|
||||
* The autologout manager service.
|
||||
*/
|
||||
public function __construct(AutologoutManagerInterface $autologout) {
|
||||
$this->autoLogoutManager = $autologout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('autologout.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback that performs the actual logout and redirects the user.
|
||||
*/
|
||||
public function ahahLogout() {
|
||||
$this->autoLogoutManager->logout();
|
||||
$response = new AjaxResponse();
|
||||
$response->setStatusCode(200);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback to reset the last access session variable.
|
||||
*/
|
||||
public function ahahSetLast() {
|
||||
$_SESSION['autologout_last'] = REQUEST_TIME;
|
||||
|
||||
// Reset the timer.
|
||||
$response = new AjaxResponse();
|
||||
$markup = $this->autoLogoutManager->createTimer();
|
||||
$response->addCommand(new Ajax\ReplaceCommand('#timer', $markup));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback that returns the time remaining for this user is logged out.
|
||||
*/
|
||||
public function ahahGetRemainingTime() {
|
||||
$time_remaining_ms = $this->autoLogoutManager->getRemainingTime() * 1000;
|
||||
|
||||
// Reset the timer.
|
||||
$response = new AjaxResponse();
|
||||
$markup = $this->autoLogoutManager->createTimer();
|
||||
|
||||
$response->addCommand(new Ajax\ReplaceCommand('#timer', $markup));
|
||||
$response->addCommand(new Ajax\SettingsCommand(['time' => $time_remaining_ms]));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\EventSubscriber;
|
||||
|
||||
use Drupal\autologout\AutologoutManagerInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Defines autologout Subscriber.
|
||||
*/
|
||||
class AutologoutSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The autologout manager service.
|
||||
*
|
||||
* @var \Drupal\autologout\AutologoutManagerInterface
|
||||
*/
|
||||
protected $autoLogoutManager;
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutSubscriber object.
|
||||
*
|
||||
* @param \Drupal\autologout\AutologoutManagerInterface $autologout
|
||||
* The autologout manager service.
|
||||
*/
|
||||
public function __construct(AutologoutManagerInterface $autologout) {
|
||||
$this->autoLogoutManager = $autologout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for autologout JS.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||
* The request event.
|
||||
*/
|
||||
public function onRequest(GetResponseEvent $event) {
|
||||
$autologout_manager = \Drupal::service('autologout.manager');
|
||||
|
||||
$uid = \Drupal::currentUser()->id();
|
||||
|
||||
if ($uid == 0) {
|
||||
if (!empty($_GET['autologout_timeout']) && $_GET['autologout_timeout'] == 1 && empty($_POST)) {
|
||||
$autologout_manager->inactivityMessage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->autoLogoutManager->preventJs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = REQUEST_TIME;
|
||||
// Check if anything wants to be refresh only. This URL would include the
|
||||
// javascript but will keep the login alive whilst that page is opened.
|
||||
$refresh_only = $autologout_manager->refreshOnly();
|
||||
$settings = \Drupal::config('autologout.settings');
|
||||
$timeout = $autologout_manager->getUserTimeout();
|
||||
$timeout_padding = $settings->get('padding');
|
||||
|
||||
// We need a backup plan if JS is disabled.
|
||||
if (!$refresh_only && isset($_SESSION['autologout_last'])) {
|
||||
// If time since last access is > timeout + padding, log them out.
|
||||
$diff = $now - $_SESSION['autologout_last'];
|
||||
if ($diff >= ($timeout + (int) $timeout_padding)) {
|
||||
$autologout_manager->logout();
|
||||
// User has changed so force Drupal to remake decisions based on user.
|
||||
global $theme, $theme_key;
|
||||
drupal_static_reset();
|
||||
$theme = NULL;
|
||||
$theme_key = NULL;
|
||||
\Drupal::theme()->getActiveTheme();
|
||||
$autologout_manager->inactivityMessage();
|
||||
}
|
||||
else {
|
||||
$_SESSION['autologout_last'] = $now;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$_SESSION['autologout_last'] = $now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[KernelEvents::REQUEST][] = ['onRequest', 100];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Form;
|
||||
|
||||
use Drupal\autologout\AutologoutManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a settings for autologout module.
|
||||
*/
|
||||
class AutologoutBlockForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The autologout manager service.
|
||||
*
|
||||
* @var \Drupal\autologout\AutologoutManagerInterface
|
||||
*/
|
||||
protected $autoLogoutManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'autologout_block_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutBlockForm object.
|
||||
*
|
||||
* @param \Drupal\autologout\AutologoutManagerInterface $autologout
|
||||
* The autologout manager service.
|
||||
*/
|
||||
public function __construct(AutologoutManagerInterface $autologout) {
|
||||
$this->autoLogoutManager = $autologout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('autologout.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['reset'] = [
|
||||
'#type' => 'button',
|
||||
'#value' => t('Reset Timeout'),
|
||||
'#weight' => 1,
|
||||
'#limit_validation_errors' => FALSE,
|
||||
'#executes_submit_callback' => FALSE,
|
||||
'#ajax' => [
|
||||
'callback' => 'autologout_ahah_set_last',
|
||||
],
|
||||
];
|
||||
|
||||
$form['timer'] = [
|
||||
'#markup' => $this->autoLogoutManager->createTimer(),
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// Submits on block form.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides settings for autologout module.
|
||||
*/
|
||||
class AutologoutSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The module manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutSettingsForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module manager service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct($config_factory);
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditableConfigNames() {
|
||||
return ['autologout.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'autologout_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('autologout.settings');
|
||||
$form['timeout'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Timeout value in seconds'),
|
||||
'#default_value' => $config->get('timeout'),
|
||||
'#size' => 8,
|
||||
'#weight' => -10,
|
||||
'#description' => $this->t('The length of inactivity time, in seconds, before automated log out. Must be 60 seconds or greater. Will not be used if role timeout is activated.'),
|
||||
];
|
||||
|
||||
$form['max_timeout'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Max timeout setting'),
|
||||
'#default_value' => $config->get('max_timeout'),
|
||||
'#size' => 10,
|
||||
'#maxlength' => 12,
|
||||
'#weight' => -8,
|
||||
'#description' => $this->t('The maximum logout threshold time that can be set by users who have the permission to set user level timeouts.'),
|
||||
];
|
||||
|
||||
$form['padding'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Timeout padding'),
|
||||
'#default_value' => $config->get('padding'),
|
||||
'#size' => 8,
|
||||
'#weight' => -6,
|
||||
'#description' => $this->t('How many seconds to give a user to respond to the logout dialog before ending their session.'),
|
||||
];
|
||||
|
||||
$form['role_logout'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Role Timeout'),
|
||||
'#default_value' => $config->get('role_logout'),
|
||||
'#weight' => -4,
|
||||
'#description' => $this->t('Enable each role to have its own timeout threshold, a refresh maybe required for changes to take effect. Any role not ticked will use the default timeout value. Any role can have a value of 0 which means that they will never be logged out.'),
|
||||
];
|
||||
|
||||
$form['redirect_url'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Redirect URL at logout'),
|
||||
'#default_value' => $config->get('redirect_url'),
|
||||
'#size' => 40,
|
||||
'#description' => $this->t('Send users to this internal page when they are logged out.'),
|
||||
];
|
||||
|
||||
$form['no_dialog'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Do not display the logout dialog'),
|
||||
'#default_value' => $config->get('no_dialog'),
|
||||
'#description' => $this->t('Enable this if you want users to logout right away and skip displaying the logout dialog.'),
|
||||
];
|
||||
|
||||
$form['use_alt_logout_method'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Use alternate logout method'),
|
||||
'#default_value' => $config->get('use_alt_logout_method'),
|
||||
'#description' => $this->t('Normally when auto logout is triggered, it is done via an AJAX service call. Sites that use an SSO provider, such as CAS, are likely to see this request fail with the error "Origin is not allowed by Access-Control-Allow-Origin". The alternate approach is to have the auto logout trigger a page redirect to initiate the logout process instead.'),
|
||||
];
|
||||
|
||||
$form['message'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Message to display in the logout dialog'),
|
||||
'#default_value' => $config->get('message'),
|
||||
'#size' => 40,
|
||||
'#description' => $this->t('This message must be plain text as it might appear in a JavaScript confirm dialog.'),
|
||||
];
|
||||
|
||||
$form['inactivity_message'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Message to display to the user after they are logged out.'),
|
||||
'#default_value' => $config->get('inactivity_message'),
|
||||
'#size' => 40,
|
||||
'#description' => $this->t('This message is displayed after the user was logged out due to inactivity. You can leave this blank to show no message to the user.'),
|
||||
];
|
||||
|
||||
$form['use_watchdog'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enable watchdog Automated Logout logging'),
|
||||
'#default_value' => $config->get('use_watchdog'),
|
||||
'#description' => $this->t('Enable logging of automatically logged out users'),
|
||||
];
|
||||
|
||||
$form['enforce_admin'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Enforce auto logout on admin pages'),
|
||||
'#default_value' => $config->get('enforce_admin'),
|
||||
'#description' => $this->t('If checked, then users will be automatically logged out when administering the site.'),
|
||||
];
|
||||
|
||||
if ($this->moduleHandler->moduleExists('jstimer') && $this->moduleHandler->moduleExists('jst_timer')) {
|
||||
$form['jstimer_format'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Autologout block time format'),
|
||||
'#default_value' => $config->get('jstimer_format'),
|
||||
'#description' => $this->t('Change the display of the dynamic timer. Available replacement values are: %day%, %month%, %year%, %dow%, %moy%, %years%, %ydays%, %days%, %hours%, %mins%, and %secs%.'),
|
||||
];
|
||||
}
|
||||
|
||||
$form['table'] = [
|
||||
'#type' => 'table',
|
||||
'#weight' => -2,
|
||||
'#header' => [
|
||||
'enable' => $this->t('Enable'),
|
||||
'name' => $this->t('Role Name'),
|
||||
'timeout' => $this->t('Timeout (seconds)'),
|
||||
],
|
||||
'#title' => $this->t('If Enabled every user in role will be logged out based on that roles timeout, unless the user has an individual timeout set.'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
// Only show this field when the 'role_logout' checkbox is enabled.
|
||||
':input[name="role_logout"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach (user_roles(TRUE) as $key => $role) {
|
||||
$form['table'][$key] = [
|
||||
'enabled' => [
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->config('autologout.role.' . $key)->get('enabled'),
|
||||
],
|
||||
'role' => [
|
||||
'#type' => 'item',
|
||||
'#value' => $key,
|
||||
'#markup' => $key,
|
||||
],
|
||||
'timeout' => [
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $this->config('autologout.role.' . $key)->get('timeout'),
|
||||
'#size' => 8,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
/**
|
||||
* Validate timeout range.
|
||||
*
|
||||
* Checks to see if timeout threshold is outside max/min values. Done here
|
||||
* to centralize and stop repeated code. Hard coded min, configurable max.
|
||||
*
|
||||
* @param int $timeout
|
||||
* The timeout value in seconds to validate.
|
||||
* @param int $max_timeout
|
||||
* (optional) Maximum value of timeout. If not set, system default is used.
|
||||
*
|
||||
* @return bool
|
||||
* Return TRUE or FALSE
|
||||
*/
|
||||
public function timeoutValidate($timeout, $max_timeout = NULL) {
|
||||
$validate = TRUE;
|
||||
if (is_null($max_timeout)) {
|
||||
$max_timeout = $this->config('autologout.settings')->get('max_timeout');
|
||||
}
|
||||
|
||||
if (!is_numeric($timeout) || $timeout < 0 || ($timeout > 0 && $timeout < 60) || $timeout > $max_timeout) {
|
||||
// Less than 60, greater than max_timeout and is numeric.
|
||||
// 0 is allowed now as this means no timeout.
|
||||
$validate = FALSE;
|
||||
}
|
||||
return $validate;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$values = $form_state->getValues();
|
||||
$new_stack = [];
|
||||
foreach ($values['table'] as $key => $pair) {
|
||||
if (is_array($pair)) {
|
||||
foreach ($pair as $pairkey => $pairvalue) {
|
||||
$new_stack[$key][$pairkey] = $pairvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$max_timeout = $values['max_timeout'];
|
||||
|
||||
if ($values['role_logout']) {
|
||||
// Validate timeouts for each role.
|
||||
foreach (array_keys(user_roles(TRUE)) as $role) {
|
||||
if (empty($new_stack[$role]) || $new_stack[$role]['enabled'] == 0) {
|
||||
// Don't validate role timeouts for non enabled roles.
|
||||
continue;
|
||||
}
|
||||
|
||||
$timeout = $new_stack[$role]['timeout'];
|
||||
$validate = $this->timeoutValidate($timeout, $max_timeout);
|
||||
if (!$validate) {
|
||||
$form_state->setErrorByName('table][' . $role . '][timeout', $this->t('%role role timeout must be an integer greater than 60, less then %max or 0 to disable autologout for that role.', ['%role' => $role, '%max' => $max_timeout]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$timeout = $values['timeout'];
|
||||
// Validate timeout.
|
||||
if ($timeout < 60) {
|
||||
$form_state->setErrorByName('timeout', $this->t('The timeout value must be an integer 60 seconds or greater.'));
|
||||
}
|
||||
elseif ($max_timeout <= 60) {
|
||||
$form_state->setErrorByName('max_timeout', $this->t('The max timeout must be an integer greater than 60.'));
|
||||
}
|
||||
elseif (!is_numeric($timeout) || ((int) $timeout != $timeout) || $timeout < 60 || $timeout > $max_timeout) {
|
||||
$form_state->setErrorByName('timeout', $this->t('The timeout must be an integer greater than 60 and less then %max.', ['%max' => $max_timeout]));
|
||||
}
|
||||
|
||||
$redirect_url = $values['redirect_url'];
|
||||
|
||||
// Validate redirect url.
|
||||
if (strpos($redirect_url, '/') !== 0) {
|
||||
$form_state->setErrorByName('redirect_url', $this->t("The user-entered string :redirect_url must begin with a '/'", [':redirect_url' => $redirect_url]));
|
||||
}
|
||||
|
||||
parent::validateForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$values = $form_state->getValues();
|
||||
$autologout_settings = $this->config('autologout.settings');
|
||||
|
||||
$autologout_settings->set('timeout', $values['timeout'])
|
||||
->set('max_timeout', $values['max_timeout'])
|
||||
->set('padding', $values['padding'])
|
||||
->set('role_logout', $values['role_logout'])
|
||||
->set('redirect_url', $values['redirect_url'])
|
||||
->set('no_dialog', $values['no_dialog'])
|
||||
->set('message', $values['message'])
|
||||
->set('inactivity_message', $values['inactivity_message'])
|
||||
->set('enforce_admin', $values['enforce_admin'])
|
||||
->set('use_alt_logout_method', $values['use_alt_logout_method'])
|
||||
->set('use_watchdog', $values['use_watchdog'])
|
||||
->save();
|
||||
|
||||
foreach ($values['table'] as $user) {
|
||||
$this->configFactory()->getEditable('autologout.role.' . $user['role'])
|
||||
->set('enabled', $user['enabled'])
|
||||
->set('timeout', $user['timeout'])
|
||||
->save();
|
||||
}
|
||||
|
||||
if (isset($values['jstimer_format'])) {
|
||||
$autologout_settings->set('jstimer_format', $values['jstimer_format'])->save();
|
||||
}
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides an 'Automated Logout info' block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "autologout_warning_block",
|
||||
* admin_label = @Translation("Automated logout info"),
|
||||
* category = @Translation("User"),
|
||||
* )
|
||||
*/
|
||||
class AutologoutWarningBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The module manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The date formatter service.
|
||||
*
|
||||
* @var \Drupal\Core\Datetime\DateFormatterInterface
|
||||
*/
|
||||
protected $dateFormatter;
|
||||
|
||||
/**
|
||||
* The config object for 'autologout.settings'.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $autoLogoutSettings;
|
||||
|
||||
/**
|
||||
* Constructs an AutologoutWarningBlock object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module manager service.
|
||||
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
|
||||
* The date formatter service.
|
||||
* @param \Drupal\Core\Config\Config $autologout_settings
|
||||
* The config object for 'autologout.settings'.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, DateFormatterInterface $date_formatter, Config $autologout_settings) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->autoLogoutSettings = $autologout_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('module_handler'),
|
||||
$container->get('date.formatter'),
|
||||
$container->get('config.factory')->get('autologout.settings')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
// @todo: This is not the place where we should be doing this.
|
||||
$return = [];
|
||||
//if ($this->moduleHandler->moduleExists('jstimer')) {
|
||||
// if (!$this->moduleHandler->moduleExists(('jst_timer'))) {
|
||||
// drupal_set_message($this->t('The "Widget: timer" module must also be enabled for the dynamic countdown to work in the automated logout block.'), 'error');
|
||||
// }
|
||||
|
||||
// if ($this->autoLogoutSettings->get('jstimer_js_load_option') != 1) {
|
||||
// drupal_set_message($this->t("The Javascript timer module's 'Javascript load options' setting should be set to 'Every page' for the dynamic countdown to work in the automated logout block."), 'error');
|
||||
// }
|
||||
//}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$autologout_manager = \Drupal::service('autologout.manager');
|
||||
if ($autologout_manager->preventJs()) {
|
||||
|
||||
// Don't display the block if the user is not going
|
||||
// to be logged out on this page.
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($autologout_manager->refreshOnly()) {
|
||||
$markup = $this->t('Autologout does not apply on the current page,
|
||||
you will be kept logged in whilst this page remains open.');
|
||||
}
|
||||
elseif ($this->moduleHandler->moduleExists('jstimer') && $this->moduleHandler->moduleExists('jst_timer')) {
|
||||
return \Drupal::formBuilder()->getForm('Drupal\autologout\Form\AutologoutBlockForm');
|
||||
}
|
||||
else {
|
||||
$timeout = (int) $this->autoLogoutSettings->get('timeout');
|
||||
$markup = $this->t('You will be logged out in @time if this page is not refreshed before then.', ['@time' => $this->dateFormatter->formatInterval($timeout)]);
|
||||
}
|
||||
|
||||
return [
|
||||
'#type' => 'markup',
|
||||
'#markup' => $markup,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Test the Autologout ajax endpoints.
|
||||
*
|
||||
* @description Ensure the AJAX endpoints work as expected
|
||||
*
|
||||
* @group Autologout
|
||||
*/
|
||||
class AutologoutAjaxTest extends WebTestBase {
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'system_test',
|
||||
'views',
|
||||
'user',
|
||||
'autologout',
|
||||
'menu_ui',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* User with admin rights.
|
||||
*/
|
||||
protected $privilegedUser;
|
||||
|
||||
/**
|
||||
* SetUp() performs any pre-requisite tasks that need to happen.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
// Create and log in our privileged user.
|
||||
$this->privilegedUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'administer site configuration',
|
||||
'access site reports',
|
||||
'access administration pages',
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'administer autologout',
|
||||
'change own logout threshold',
|
||||
]);
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
|
||||
// Make node page default.
|
||||
$this->config('system.site')->set('page.front', 'node')->save();
|
||||
// Place the User login block on the home page to verify Log out text.
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ajax logout callbacks work as expected.
|
||||
*/
|
||||
public function testAutologoutByAjax() {
|
||||
|
||||
$config = \Drupal::configFactory()->getEditable('autologout.settings');
|
||||
$config->set('timeout', 100)
|
||||
->set('padding', 10)
|
||||
->save();
|
||||
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in.');
|
||||
|
||||
// Test the time remaining callback works as expected.
|
||||
$result = $this->drupalGetAjax('autologout_ajax_get_time_left');
|
||||
$this->assertResponse(200, 'autologout_ajax_get_time_left is accessible when logged in');
|
||||
$this->assertEqual('insert', $result[0]['command'], 'autologout_ajax_get_time_left returns an insert command for adding the jstimer onto the page');
|
||||
$this->assertEqual('#timer', $result[0]['selector'], 'autologout_ajax_get_time_left specifies the #timer selector.');
|
||||
$this->assert(!empty($result[1]['settings']['time']) && is_int($result[1]['settings']['time']) && $result[1]['settings']['time'] > 0, 'autologout_ajax_get_time_left returns the remaining time as a positive integer');
|
||||
|
||||
// Test that ajax logout works as expected.
|
||||
$this->drupalGet('autologout_ahah_logout');
|
||||
$this->assertResponse(200, 'autologout_ahah_logout is accessible when logged in');
|
||||
|
||||
// Check we are now logged out.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertNoText(t('Log out'), 'User is no longer logged in.');
|
||||
|
||||
// Check further get time remaining requests return access denied.
|
||||
$this->drupalGet('autologout_ajax_get_time_left');
|
||||
$this->assertResponse(403, 'autologout_ajax_get_time_left is not accessible when logged out.');
|
||||
|
||||
// Check further logout requests result in access denied.
|
||||
$this->drupalGet('autologout_ahah_logout');
|
||||
$this->assertResponse(403, 'autologout_ahah_logout is not accessible when logged out.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ajax stay logged in callbacks work as expected.
|
||||
*/
|
||||
public function testStayloggedInByAjax() {
|
||||
$config = \Drupal::configFactory()->getEditable('autologout.settings');
|
||||
$config->set('timeout', 20)
|
||||
->set('padding', 5)
|
||||
->save();
|
||||
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in.');
|
||||
|
||||
// Sleep for half the timeout.
|
||||
sleep(14);
|
||||
|
||||
// Test that ajax stay logged in works.
|
||||
$result = $this->drupalGetAjax('autologout_ahah_set_last');
|
||||
$this->assertResponse(200, 'autologout_ahah_set_last is accessible when logged in.');
|
||||
$this->assertEqual('insert', $result[0]['command'], 'autologout_ajax_set_last returns an insert command for adding the jstimer onto the page');
|
||||
$this->assertEqual('#timer', $result[0]['selector'], 'autologout_ajax_set_last specifies the #timer selector.');
|
||||
|
||||
// Sleep for half the timeout again.
|
||||
sleep(14);
|
||||
|
||||
// Check we are still logged in.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, t('Homepage is accessible'));
|
||||
$this->assertText(t('Log out'), t('User is still logged in.'));
|
||||
|
||||
// Logout.
|
||||
$this->drupalGet('autologout_ahah_logout');
|
||||
$this->assertResponse(200, 'autologout_ahah_logout is accessible when logged in.');
|
||||
|
||||
// Check further requests to set last result in 403.
|
||||
$result = $this->drupalGetAjax('autologout_ahah_set_last');
|
||||
$this->assertResponse(403, 'autologout_ahah_set_last is not accessible when logged out.');
|
||||
}
|
||||
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Tests;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Test session cleanup on login.
|
||||
*
|
||||
* @description Ensure that the autologout module cleans up stale sessions at login
|
||||
*
|
||||
* @group Autologout
|
||||
*/
|
||||
class AutologoutSessionCleanupOnLoginTest extends WebTestBase {
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['autologout', 'node'];
|
||||
/**
|
||||
* A store references to different sessions.
|
||||
*/
|
||||
protected $curlHandles = [];
|
||||
protected $loggedInUsers = [];
|
||||
protected $privilegedUser;
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* SetUp() performs any pre-requisite tasks that need to happen.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
// Create and log in our privileged user.
|
||||
$this->privilegedUser = $this->drupalCreateUser(['access content overview',
|
||||
'administer site configuration',
|
||||
'access site reports',
|
||||
'access administration pages',
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'administer autologout',
|
||||
'change own logout threshold',
|
||||
]);
|
||||
$this->database = $this->container->get('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that stale sessions are cleaned up at login.
|
||||
*/
|
||||
public function testSessionCleanupAtLogin() {
|
||||
// For the purposes of the test, set the timeout periods to 5 seconds.
|
||||
$config = \Drupal::configFactory()->getEditable('autologout.settings');
|
||||
$config->set('timeout', 5)
|
||||
->set('padding', 0)
|
||||
->save();
|
||||
|
||||
// Login in session 1.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
// Check one active session.
|
||||
$sessions = $this->getSessions($this->privilegedUser);
|
||||
$this->assertEqual(1, count($sessions), 'After initial login there is one active session');
|
||||
|
||||
// Switch sessions.
|
||||
$session1 = $this->stashSession();
|
||||
|
||||
// Login to session 2.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
|
||||
// Check two active sessions.
|
||||
$sessions = $this->getSessions($this->privilegedUser);
|
||||
$this->assertEqual(2, count($sessions), 'After second login there is now two active session');
|
||||
|
||||
$this->stashSession();
|
||||
|
||||
// Switch sessions.
|
||||
// Wait for sessions to expire.
|
||||
sleep(6);
|
||||
|
||||
// Login to session 3.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
|
||||
// Check one active session.
|
||||
$sessions = $this->getSessions($this->privilegedUser);
|
||||
$this->assertEqual(1, count($sessions), 'After third login, there is 1 active session, two stale sessions were cleaned up.');
|
||||
|
||||
// Switch back to session 1 and check no longer logged in.
|
||||
$this->restoreSession($session1);
|
||||
$this->drupalGet('node');
|
||||
$this->assertNoText(t('Log out'), 'User is no longer logged in on session 1.');
|
||||
|
||||
$this->closeAllSessions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active sessions for given user.
|
||||
*/
|
||||
public function getSessions($account) {
|
||||
// Check there is one session in the sessions table.
|
||||
$result = $this->database->select('sessions', 's')
|
||||
->fields('s')
|
||||
->condition('uid', $account->id())
|
||||
->orderBy('timestamp', 'DESC')
|
||||
->execute();
|
||||
$sessions = [];
|
||||
foreach ($result as $session) {
|
||||
$sessions[] = $session;
|
||||
}
|
||||
|
||||
return $sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise a new unique session.
|
||||
*
|
||||
* @return string
|
||||
* Unique identifier for the session just stored.
|
||||
* It is the cookiefile name.
|
||||
*/
|
||||
public function stashSession() {
|
||||
if (empty($this->cookieFile)) {
|
||||
// No session to stash.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The session_id is the current cookieFile.
|
||||
$session_id = $this->cookieFile;
|
||||
|
||||
$this->curlHandles[$session_id] = $this->curlHandle;
|
||||
$this->loggedInUsers[$session_id] = $this->loggedInUser;
|
||||
|
||||
// Reset Curl.
|
||||
unset($this->curlHandle);
|
||||
$this->loggedInUser = FALSE;
|
||||
|
||||
// Set a new unique cookie filename.
|
||||
do {
|
||||
$this->cookieFile = $this->originalFileDirectory . '/' . $this->randomMachineName() . '.jar';
|
||||
} while (isset($this->curlHandles[$this->cookieFile]));
|
||||
|
||||
return $session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a previously stashed session.
|
||||
*
|
||||
* @param string $session_id
|
||||
* The session to restore as returned by stashSession();
|
||||
* This is also the path to the cookie file.
|
||||
*
|
||||
* @return string
|
||||
* The old session id that was replaced.
|
||||
*/
|
||||
public function restoreSession($session_id) {
|
||||
$old_session_id = NULL;
|
||||
|
||||
if (isset($this->curlHandle)) {
|
||||
$old_session_id = $this->stashSession();
|
||||
}
|
||||
|
||||
// Restore the specified session.
|
||||
$this->curlHandle = $this->curlHandles[$session_id];
|
||||
$this->cookieFile = $session_id;
|
||||
$this->loggedInUser = $this->loggedInUsers[$session_id];
|
||||
|
||||
return $old_session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all stashed sessions and the current session.
|
||||
*/
|
||||
public function closeAllSessions() {
|
||||
foreach ($this->curlHandles as $curl_handle) {
|
||||
if (isset($curl_handle)) {
|
||||
curl_close($curl_handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Make the server forget all sessions.
|
||||
$this->database->truncate('sessions')->execute();
|
||||
|
||||
$this->curlHandles = [];
|
||||
$this->loggedInUsers = [];
|
||||
$this->loggedInUser = FALSE;
|
||||
$this->cookieFile = $this->originalFileDirectory . '/' . $this->randomMachineName() . '.jar';
|
||||
unset($this->curlHandle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\autologout\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the autologout's features.
|
||||
*
|
||||
* @description Ensure that the autologout module functions as expected
|
||||
*
|
||||
* @group Autologout
|
||||
*/
|
||||
class AutologoutTest extends WebTestBase {
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'system',
|
||||
'system_test',
|
||||
'views',
|
||||
'user',
|
||||
'autologout',
|
||||
'menu_ui',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* Use the Standard profile to test help implementations of many core modules.
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* User with admin rights.
|
||||
*/
|
||||
protected $privilegedUser;
|
||||
|
||||
/**
|
||||
* The config factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Stores the user data service used by the test.
|
||||
*
|
||||
* @var \Drupal\user\UserDataInterface
|
||||
*/
|
||||
public $userData;
|
||||
|
||||
/**
|
||||
* SetUp() performs any pre-requisite tasks that need to happen.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
// Create and log in our privileged user.
|
||||
$this->privilegedUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'administer site configuration',
|
||||
'access site reports',
|
||||
'access administration pages',
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
'administer nodes',
|
||||
'administer autologout',
|
||||
'change own logout threshold',
|
||||
'access site reports',
|
||||
'view the administration theme',
|
||||
]);
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
|
||||
$this->configFactory = $this->container->get('config.factory');
|
||||
$this->userData = $this->container->get('user.data');
|
||||
|
||||
$config = $this->configFactory->getEditable('autologout.settings');
|
||||
// For the purposes of the test, set the timeout periods to 10 seconds.
|
||||
$config->set('timeout', 10)
|
||||
->save();
|
||||
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
|
||||
// Make node page default.
|
||||
$this->configFactory->getEditable('system.site')->set('page.front', 'node')->save();
|
||||
// Place the User login block on the home page to verify Log out text.
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the precedence of the timeouts.
|
||||
*
|
||||
* This tests the following function:
|
||||
* _autologout_get_user_timeout();
|
||||
*/
|
||||
public function testAutologoutTimeoutPrecedence() {
|
||||
$autologout_settings = $this->configFactory->getEditable('autologout.settings');
|
||||
$autologout_role_settings = $this->configFactory->getEditable('autologout.role.authenticated');
|
||||
$uid = $this->privilegedUser->id();
|
||||
$autologout_user_settings = \Drupal::service('user.data');
|
||||
|
||||
// Default used if no role is specified.
|
||||
$autologout_settings->set('timeout', 100)
|
||||
->set('role_logout', FALSE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', FALSE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$this->assertAutotimeout($uid, 100, 'User timeout uses default if no other option set');
|
||||
|
||||
// Default used if role selected but no user's role is selected.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', FALSE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$this->assertAutotimeout($uid, 100, 'User timeout uses default if role timeouts are used but not one of the current user.');
|
||||
|
||||
// Role timeout is used if user's role is selected.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', TRUE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$this->assertAutotimeout($uid, 200, 'User timeout uses role value');
|
||||
|
||||
// Role timeout is used if user's role is selected.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', TRUE)
|
||||
->set('timeout', 0)
|
||||
->save();
|
||||
$this->assertAutotimeout($uid, 0, 'User timeout uses role value of 0 if set for one of the user roles.');
|
||||
|
||||
// Role timeout used if personal timeout is empty string.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', TRUE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$autologout_user_settings->set('autologout', $uid, 'timeout', '');
|
||||
$autologout_user_settings->set('autologout', $uid, 'enabled', FALSE);
|
||||
$this->assertAutotimeout($uid, 200, 'User timeout uses role value if personal value is the empty string.');
|
||||
|
||||
// Default timeout used if personal timeout is empty string.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', FALSE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$autologout_user_settings->set('autologout', $uid, 'timeout', '');
|
||||
$autologout_user_settings->set('autologout', $uid, 'enabled', FALSE);
|
||||
$this->assertAutotimeout($uid, 100, 'User timeout uses default value if personal value is the empty string and no role timeout is specified.');
|
||||
|
||||
// Personal timeout used if set.
|
||||
$autologout_settings->set('role_logout', TRUE)
|
||||
->save();
|
||||
$autologout_role_settings->set('enabled', FALSE)
|
||||
->set('timeout', 200)
|
||||
->save();
|
||||
$autologout_user_settings->set('autologout', $uid, 'timeout', 300);
|
||||
$autologout_user_settings->set('autologout', $uid, 'enabled', TRUE);
|
||||
$this->assertAutotimeout($uid, 300, 'User timeout uses default value if personal value is the empty string and no role timeout is specified.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a user is logged out after the default timeout period.
|
||||
*/
|
||||
public function testAutologoutDefaultTimeout() {
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText('Log out', 'User is still logged in.');
|
||||
|
||||
// Wait for timeout period to elapse.
|
||||
sleep(30);
|
||||
|
||||
// Check we are now logged out.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertNoRaw(t('Log out'), 'User is no longer logged in.');
|
||||
$this->assertText(t('You have been logged out due to inactivity.'), 'User sees inactivity message.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a user is not logged out within the default timeout period.
|
||||
*/
|
||||
public function testAutologoutNoLogoutInsideTimeout() {
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in.');
|
||||
|
||||
// Wait within the timeout period.
|
||||
sleep(10);
|
||||
|
||||
// Check we are still logged in.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in.');
|
||||
$this->assertNoText(t('You have been logged out due to inactivity.'), 'User does not see inactivity message.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of the settings for submission.
|
||||
*/
|
||||
public function testAutologoutSettingsForm() {
|
||||
$edit = [];
|
||||
$autologout_settings = $this->configFactory->getEditable('autologout.settings');
|
||||
$autologout_settings->set('max_timeout', 1000)
|
||||
->save();
|
||||
|
||||
$roles = user_roles(TRUE);
|
||||
// Unset authenticated, as it will be used to add manual value later.
|
||||
unset($roles['authenticated']);
|
||||
|
||||
// Test that it is possible to set a value above the max_timeout
|
||||
// threshold.
|
||||
$edit['timeout'] = 1500;
|
||||
$edit['max_timeout'] = 2000;
|
||||
$edit['padding'] = 60;
|
||||
$edit['role_logout'] = TRUE;
|
||||
$edit['table[authenticated][enabled]'] = TRUE;
|
||||
$edit['table[authenticated][timeout]'] = 1200;
|
||||
foreach ($roles as $key => $role) {
|
||||
$edit['table[' . $key . '][enabled]'] = TRUE;
|
||||
$edit['table[' . $key . '][timeout]'] = 1200;
|
||||
}
|
||||
$edit['redirect_url'] = '/user/login';
|
||||
|
||||
$this->drupalPostForm('admin/config/people/autologout', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'), 'Unable to save autologout config when modifying the max timeout.');
|
||||
|
||||
// Test that out of range values are picked up.
|
||||
$edit['timeout'] = 2500;
|
||||
$edit['max_timeout'] = 2000;
|
||||
$edit['padding'] = 60;
|
||||
$edit['role_logout'] = TRUE;
|
||||
$edit['table[authenticated][enabled]'] = TRUE;
|
||||
$edit['table[authenticated][timeout]'] = 1200;
|
||||
foreach ($roles as $key => $role) {
|
||||
$edit['table[' . $key . '][enabled]'] = TRUE;
|
||||
$edit['table[' . $key . '][timeout]'] = 1200;
|
||||
}
|
||||
$edit['redirect_url'] = '/user/login';
|
||||
$this->drupalPostForm('admin/config/people/autologout', $edit, t('Save configuration'));
|
||||
$this->assertNoText(t('The configuration options have been saved.'), 'Saved configuration despite the autologout_timeout being too large.');
|
||||
|
||||
// Test that out of range values are picked up.
|
||||
$edit['timeout'] = 1500;
|
||||
$edit['max_timeout'] = 2000;
|
||||
$edit['padding'] = 60;
|
||||
$edit['role_logout'] = TRUE;
|
||||
$edit['table[authenticated][enabled]'] = TRUE;
|
||||
$edit['table[authenticated][timeout]'] = 2500;
|
||||
foreach ($roles as $key => $role) {
|
||||
$edit['table[' . $key . '][enabled]'] = TRUE;
|
||||
$edit['table[' . $key . '][timeout]'] = 1200;
|
||||
}
|
||||
$edit['redirect_url'] = '/user/login';
|
||||
$this->drupalPostForm('admin/config/people/autologout', $edit, t('Save configuration'));
|
||||
$this->assertNoText(t('The configuration options have been saved.'), 'Saved configuration despite a role timeout being too large.');
|
||||
|
||||
// Test that role timeouts are not validated for disabled roles.
|
||||
$edit['timeout'] = 1500;
|
||||
$edit['max_timeout'] = 2000;
|
||||
$edit['padding'] = 60;
|
||||
$edit['role_logout'] = TRUE;
|
||||
$edit['table[authenticated][enabled]'] = FALSE;
|
||||
$edit['table[authenticated][timeout]'] = 4000;
|
||||
foreach ($roles as $key => $role) {
|
||||
$edit['table[' . $key . '][enabled]'] = FALSE;
|
||||
$edit['table[' . $key . '][timeout]'] = 1200;
|
||||
}
|
||||
$edit['redirect_url'] = '/user/login';
|
||||
|
||||
$this->drupalPostForm('admin/config/people/autologout', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'), 'Unable to save autologout due to out of range role timeout for a role which is not enabled..');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a user is logged out and denied access to admin pages.
|
||||
*/
|
||||
public function testAutologoutDefaultTimeoutAccessDeniedToAdmin() {
|
||||
$autologout_settings = $this->configFactory->getEditable('autologout.settings');
|
||||
// Enforce auto logout of admin pages.
|
||||
$autologout_settings->set('enforce_admin', FALSE)
|
||||
->save();
|
||||
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertResponse(200, 'Admin page is accessible');
|
||||
$this->assertText(t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation."), 'User can access elements of the admin page.');
|
||||
|
||||
// Wait for timeout period to elapse.
|
||||
sleep(30);
|
||||
|
||||
// Check we are now logged out.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertResponse(403, 'Admin page returns 403 access denied.');
|
||||
$this->assertNoText(t('Log out'), 'User is no longer logged in.');
|
||||
$this->assertNoText(t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation."), 'User cannot access elements of the admin page.');
|
||||
$this->assertText(t('You have been logged out due to inactivity.'), 'User sees inactivity message.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test integration with the remember me module.
|
||||
*
|
||||
* Users who checked remember_me on login should never be logged out.
|
||||
*/
|
||||
public function testNoAutologoutWithRememberMe() {
|
||||
// Set the remember_me module data bit to TRUE.
|
||||
$this->userData->set('remember_me', $this->privilegedUser->id(), 'remember_me', TRUE);
|
||||
|
||||
// Check that the user can access the page after login.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in.');
|
||||
|
||||
// Wait for timeout period to elapse.
|
||||
sleep(30);
|
||||
|
||||
// Check we are still logged in.
|
||||
$this->drupalGet('node');
|
||||
$this->assertResponse(200, 'Homepage is accessible');
|
||||
$this->assertText(t('Log out'), 'User is still logged in after timeout with remember_me on.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of custom message displayed on autologout.
|
||||
*/
|
||||
public function testCustomMessage() {
|
||||
$autologout_settings = $this->configFactory->getEditable('autologout.settings');
|
||||
$inactivity_message = 'Custom message for test';
|
||||
|
||||
// Update message string in configuration.
|
||||
$autologout_settings->set('inactivity_message', $inactivity_message)
|
||||
->save();
|
||||
|
||||
// Set time out for 10 seconds.
|
||||
$autologout_settings->set('timeout', 10)
|
||||
->save();
|
||||
|
||||
// Wait for 20 seconds for timeout.
|
||||
sleep(30);
|
||||
|
||||
// Access the admin page and verify user is logged out and custom message
|
||||
// is displayed.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertText($inactivity_message, 'User sees custom message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behaviour of application when Autologout is enabled for admin.
|
||||
*/
|
||||
public function testAutologoutAdminPages() {
|
||||
|
||||
$autologout_settings = $this->configFactory->getEditable('autologout.settings');
|
||||
// Enforce auto logout of admin pages.
|
||||
$autologout_settings->set('enforce_admin', TRUE)
|
||||
->save();
|
||||
// Set time out as 10 seconds.
|
||||
$autologout_settings->set('timeout', 10)
|
||||
->save();
|
||||
// Verify admin should not be logged out.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertResponse('200', 'Admin pages are accessible');
|
||||
|
||||
// Wait until timeout.
|
||||
sleep(30);
|
||||
|
||||
// Verify admin should be logged out.
|
||||
$this->drupalGet('admin/reports/status');
|
||||
$this->assertText(t('You have been logged out due to inactivity.'), 'User sees inactivity message.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the timeout for a particular user.
|
||||
*
|
||||
* @param int $uid
|
||||
* User uid to assert the timeout for.
|
||||
* @param int $expected_timeout
|
||||
* The expected timeout.
|
||||
* @param string $message
|
||||
* The test message.
|
||||
* @param string $group
|
||||
* The test grouping.
|
||||
*/
|
||||
public function assertAutotimeout($uid, $expected_timeout, $message = '', $group = '') {
|
||||
return $this->assertEqual(\Drupal::service('autologout.manager')->getUserTimeout($uid), $expected_timeout, $message, $group);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user