Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
"drupal/smtp": "^1.2",
"drupal/stage_file_proxy": "^2.0",
"drupal/tablefield": "2.4",
"drupal/tfa": "1.9",
"drupal/tfa_email_otp": "^1.0@beta",
"drupal/tfa": "^1.12",
"drupal/tfa_email_otp": "^1.0",
"drupal/token_conditions": "dev-compatible-with-d10",
"drupal/token_filter": "^2.0",
"drupal/twig_field_value": "^2.0",
Expand Down Expand Up @@ -124,7 +124,8 @@
"drupal/shield": "^1.8",
"drupal/media_alias_display": "^2.1",
"drupal/search_api_exclude_entity": "^3.0",
"drupal/default_paragraphs": "^2.0"
"drupal/default_paragraphs": "^2.0",
"drupal/view_password": "^6.0"
},
"repositories": {
"drupal": {
Expand Down
63 changes: 61 additions & 2 deletions modules/tide_dashboard/tide_dashboard.module
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
* Tide Dashboard.
*/

use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\user\UserInterface;

/**
* Implements hook_toolbar_alter().
*
Expand Down Expand Up @@ -33,9 +39,62 @@ function _tide_dashboard_workbench_content_title_callback() {
/**
* Implements hook_user_login().
*/
function tide_dashboard_user_login() {
function tide_dashboard_user_login(UserInterface $account) {
$request = \Drupal::service('request_stack')->getCurrentRequest();
if (($request->query->has('destination')) === FALSE) {
$uid = $account->id();

$module_handler = \Drupal::service('module_handler');
if ($module_handler->moduleExists('tfa')) {
// Check which roles are forced to use tfa.
$tfa_config = \Drupal::config('tfa.settings');
$required_roles = $tfa_config->get('required_roles') ?: [];

// Check if the current user has any of the required roles.
$user_roles = $account->getRoles();
$is_required = (bool) array_intersect($required_roles, $user_roles);

// If they ARE required to have it, check if they've actually set it up.
if ($is_required) {
$user_data = \Drupal::service('user.data');
$tfa_settings = $user_data->get('tfa', $uid, 'tfa_user_settings');
// Check 'status' AND ensure the 'plugins' array is not empty.
$has_active_plugins = !empty($tfa_settings['data']['plugins']);
$is_enabled = !empty($tfa_settings['status']) && $tfa_settings['status'] == 1;

// If user don't have active plugins, they haven't finished setup.
if (!$is_enabled || !$has_active_plugins) {
$request->query->set('destination', "/user/$uid/security/tfa");
return;
}
}
}

// Fallback: If not required or already setup, go to workbench.
if (!$request->query->has('destination')) {
$request->query->set('destination', '/admin/workbench');
}
}

/**
* Implements hook_system_breadcrumb_alter().
*/
function tide_dashboard_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context) {
$links = $breadcrumb->getLinks();

if (!empty($links)) {
// Check if the first link is the Home link.
if ($links[0]->getUrl()->isRouted() && $links[0]->getUrl()->getRouteName() === '<front>') {

$new_url = Url::fromUserInput('/admin/workbench');
$new_link = Link::fromTextAndUrl($links[0]->getText(), $new_url);

// Swap the first link to workbench.
$links[0] = $new_link;

$reflection = new \ReflectionClass($breadcrumb);
$property = $reflection->getProperty('links');
$property->setAccessible(TRUE);
$property->setValue($breadcrumb, $links);
}
}
}
149 changes: 149 additions & 0 deletions modules/tide_tfa/src/Form/TideTfaOverviewForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php

namespace Drupal\tide_tfa\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\tfa\Form\TfaOverviewForm;
use Drupal\user\UserInterface;

/**
* Provides a customised TFA overview form for Tide.
*
* Extends the core TFA overview form to alter the presentation and behaviour
* of multi-factor authentication setup and status within the Tide CMS.
*/
class TideTfaOverviewForm extends TfaOverviewForm {

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, UserInterface $user = NULL) {
$output = [];
$output['info'] = [
'#type' => 'markup',
'#markup' => '<p>' . $this->t('Multi-factor authentication provides
additional security for your account.</br>With multi-factor authentication enabled,
you log in to the CMS with a verification code in addition to your username and
password.') . '</p>',
];
// $form_state['storage']['account'] = $user;.
$config = $this->config('tfa.settings');
$user_tfa = $this->tfaGetTfaData($user->id(), $this->userData);
$enabled = isset($user_tfa['status']) && $user_tfa['status'];

if ($config->get('enabled')) {
$enabled = isset($user_tfa['status'], $user_tfa['data']) && !empty($user_tfa['data']['plugins']) && $user_tfa['status'];
$enabled_plugins = $user_tfa['data']['plugins'] ?? [];

$validation_plugins = $this->tfaValidation->getDefinitions();
if ($validation_plugins) {
$output['validation'] = [
'#type' => 'details',
'#title' => $this->t('Validation plugins'),
'#open' => TRUE,
];

foreach ($validation_plugins as $plugin_id => $plugin) {
if (!empty($config->get('allowed_validation_plugins')[$plugin_id])) {
$output['validation'][$plugin_id] = $this->tfaPluginSetupFormOverview($plugin, $user, !empty($enabled_plugins[$plugin_id]));
}
}
}

if ($enabled) {
$login_plugins = $this->tfaLogin->getDefinitions();
if ($login_plugins) {
$output['login'] = [
'#type' => 'details',
'#title' => $this->t('Login plugins'),
'#open' => TRUE,
'#access' => FALSE,
];

foreach ($login_plugins as $plugin_id => $plugin) {
if (!empty($config->get('login_plugins')[$plugin_id])) {
$output['login'][$plugin_id] = $this->tfaPluginSetupFormOverview($plugin, $user, TRUE);
$output['login']['#access'] = TRUE;
}
}
}

$send_plugins = $this->tfaSend->getDefinitions();
if ($send_plugins) {
$output['send'] = [
'#type' => 'details',
'#title' => $this->t('Send plugins'),
'#open' => TRUE,
];

foreach ($send_plugins as $plugin_id => $plugin) {
if (!empty($config->get('send_plugins')[$plugin_id])) {
$output['send'][$plugin_id] = $this->tfaPluginSetupFormOverview($plugin, $user, TRUE);
}
}
}
}

// Moved it inside to show the status if only TFA is enabled.
if (!empty($user_tfa)) {
if ($enabled && !empty($user_tfa['data']['plugins'])) {
$disable_url = Url::fromRoute('tfa.disable', ['user' => $user->id()]);
if ($disable_url->access()) {
$status_text = $this->t('Status: <strong>Multi-factor authentication enabled</strong>, set
@time. <a href=":url">Disable Multi-factor authentication</a>', [
'@time' => $this->dateFormatter->format($user_tfa['saved']),
':url' => $disable_url->toString(),
]);
}
else {
$status_text = $this->t('Status: Multi-factor authentication enabled');
}
}
else {
$status_text = $this->t('Status: Multi-factor authentication disabled');
}
$output['status'] = [
'#type' => 'markup',
'#markup' => '<p>' . $status_text . '</p>',
];
}

if (!$config->get('forced')) {
$validation_skipped = $user_tfa['validation_skipped'] ?? 0;

$output['validation_skip_status'] = [
'#type' => 'markup',
'#markup' => '<p>' . $this->t(
'Authentication setup: @remaining logins remain before multi-factor authentication is required',
[
'@remaining' => $config->get('validation_skip') - $validation_skipped,
]
) . '</p>',
];
}
}
else {
$output['disabled'] = [
'#type' => 'markup',
'#markup' => '<b>Currently there are no enabled plugins.</b>',
];
}

if ($this->canPerformReset($user)) {
$output['actions'] = ['#type' => 'actions'];
$output['actions']['reset_skip_attempts'] = [
'#type' => 'submit',
'#value' => $this->t('Reset skip validation attempts'),
'#submit' => ['::resetSkipValidationAttempts'],
];
$output['account'] = [
'#type' => 'value',
'#value' => $user,
];
}

return $output;
}

}
12 changes: 9 additions & 3 deletions modules/tide_tfa/src/Plugin/TfaSetup/TideTfaEmailOtpSetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ public function getSetupForm(array $form, FormStateInterface $form_state) {
$params = $form_state->getValues();
$userData = $this->userData->get('tfa', $params['account']->id(), 'tfa_email_otp');

$form['email_otp_heading'] = [
'#type' => 'html_tag',
'#tag' => 'h2',
'#value' => $this->t('Email authentication for login'),
];

// [SD-294] Changing the title and description.
$form['enabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Yes, email me a verification code every time I log in'),
'#title' => $this->t('I agree to be sent a verification code via email each time I log in.'),
'#description' => $this->t('Each single-use verification code expires after use, or after 10 minutes if not used.'),
'#required' => TRUE,
'#default_value' => $userData['enable'] ?? 0,
Expand All @@ -58,7 +64,7 @@ public function getOverview(array $params) {
// [SD-294] Modify the description.
$description = '';
if ($params['enabled']) {
$description .= $this->t('<p><b>Enabled</b></p>');
$description .= $this->t('<p><b>Multi-factor authentication enabled</b></p>');
}
$output = [
'heading' => [
Expand All @@ -77,7 +83,7 @@ public function getOverview(array $params) {
'#access' => !$params['enabled'],
'#links' => [
'admin' => [
'title' => $this->t('Enable two-factor authentication via email'),
'title' => $this->t('Enable multi-factor authentication via email'),
'url' => Url::fromRoute('tfa.validation.setup', [
'user' => $params['account']->id(),
'method' => $params['plugin_id'],
Expand Down
24 changes: 24 additions & 0 deletions modules/tide_tfa/src/Routing/TideTfaRouteSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
use Drupal\Core\Routing\RoutingEvents;

/**
* Listens to the dynamic route events.
Expand All @@ -25,6 +26,29 @@ protected function alterRoutes(RouteCollection $collection) {
if ($route = $collection->get('user.reset.login')) {
$route->setDefault('_controller', '\Drupal\tide_tfa\Controller\TideTfaUserController::doResetPassLogin');
}
// TFA overview page (User → Security → TFA).
if ($route = $collection->get('tfa.overview')) {
$route->setDefault('_title', 'Multi-factor authentication');
$route->setDefault('_form', '\Drupal\tide_tfa\Form\TideTfaOverviewForm');
}
// TFA setup page.
if ($route = $collection->get('tfa.validation.setup')) {
$route->setDefault('_title', 'Multi-factor authentication setup');
}
// TFA disable page.
if ($route = $collection->get('tfa.disable')) {
$route->setDefault('_title', 'Disable multi-factor authentication');
}
}

/**
* Attempt to be the last subscriber to allow our routes to take priority.
*/
public static function getSubscribedEvents(): array {
$events = parent::getSubscribedEvents();
// Use lower priority than tfa module.
$events[RoutingEvents::ALTER] = ['onAlterRoutes', (PHP_INT_MIN - 1)];
return $events;
}

}
25 changes: 25 additions & 0 deletions modules/tide_tfa/src/TideTfaOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,29 @@ public static function setupTfaRolePermissions() {
}
}

/**
* Setup view password.
*/
public static function setupViewPassword() {
// Enable view_password module if not already enabled.
$moduleHandler = \Drupal::service('module_handler');
$moduleInstaller = \Drupal::service('module_installer');
if (!$moduleHandler->moduleExists('view_password')) {
$moduleInstaller->install(['view_password']);
}

// Set view_password configuration.
$config = \Drupal::configFactory()->getEditable('view_password.settings');
$form_ids_string = $config->get('form_ids') ?? '';
$form_ids = array_filter(array_map('trim', explode(',', $form_ids_string)));

if (!in_array('tfa_setup', $form_ids, TRUE)) {
$form_ids[] = 'tfa_setup';
// Convert back to a comma-separated string.
// view_password only accept string for the schema.
$new_value = implode(',', $form_ids);
$config->set('form_ids', $new_value)->save();
}
}

}
11 changes: 11 additions & 0 deletions modules/tide_tfa/tide_tfa.install
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ function tide_tfa_install() {

// Setup TFA role permissions.
$tideTfaOperation->setupTfaRolePermissions();

// Setup view password.
$tideTfaOperation->setupViewPassword();
}

/**
* Setup view password.
*/
function tide_tfa_update_10000() {
$tideTfaOperation = new TideTfaOperation();
$tideTfaOperation->setupViewPassword();
}
Loading
Loading