‪TYPO3CMS  ‪main
MfaInfoElement.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 use TYPO3\CMS\Core\Imaging\IconSize;
29 
37 {
38  private const ‪ALLOWED_TABLES = ['be_users', 'fe_users'];
39 
40  public function ‪__construct(
41  private readonly ‪IconFactory $iconFactory,
42  private readonly ‪MfaProviderRegistry $mfaProviderRegistry,
43  ) {}
44 
45  public function ‪render(): array
46  {
47  $resultArray = $this->‪initializeResultArray();
48 
49  $currentBackendUser = $this->‪getBackendUser();
50  $tableName = $this->data['tableName'];
51 
52  // This renderType only works for user tables: be_users, fe_users
53  if (!in_array($tableName, self::ALLOWED_TABLES, true)) {
54  return $resultArray;
55  }
56 
57  // Initialize a user based on the current table name
58  $targetUser = $tableName === 'be_users'
59  ? GeneralUtility::makeInstance(BackendUserAuthentication::class)
60  : GeneralUtility::makeInstance(FrontendUserAuthentication::class);
61 
62  $userId = (int)($this->data['databaseRow'][$targetUser->userid_column] ?? 0);
63  $targetUser->enablecolumns = ['deleted' => true];
64  $targetUser->setBeUserByUid($userId);
65 
66  $isDeactivationAllowed = true;
67  // Providers from system maintainers can only be deactivated by system maintainers.
68  // This check is however only be necessary if the target is a backend user.
69  if ($targetUser instanceof ‪BackendUserAuthentication) {
70  $systemMaintainers = array_map(intval(...), ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? []);
71  $isTargetUserSystemMaintainer = $targetUser->isAdmin() && in_array($userId, $systemMaintainers, true);
72  if ($isTargetUserSystemMaintainer && !$currentBackendUser->isSystemMaintainer()) {
73  $isDeactivationAllowed = false;
74  }
75  }
76 
77  // Fetch providers from the mfa field
78  $mfaProviders = json_decode($this->data['parameterArray']['itemFormElValue'] ?? '', true) ?? [];
79 
80  // Initialize variables
81  $html = $childHtml = $activeProviders = $lockedProviders = [];
82  $lang = $this->‪getLanguageService();
83  $enabledLabel = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.mfa.enabled'));
84  $disabledLabel = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.mfa.disabled'));
85  $status = '<span class="badge badge-danger badge-space-end t3js-mfa-status-label" data-alternative-label="' . $enabledLabel . '">' . $disabledLabel . '</span>';
86 
87  // Unset invalid providers
88  foreach ($mfaProviders as ‪$identifier => $providerSettings) {
89  if (!$this->mfaProviderRegistry->hasProvider(‪$identifier)) {
90  unset($mfaProviders[‪$identifier]);
91  }
92  }
93 
94  if ($mfaProviders !== []) {
95  // Check if remaining providers are active and/or locked for the user
96  foreach ($mfaProviders as ‪$identifier => $providerSettings) {
97  $provider = $this->mfaProviderRegistry->getProvider(‪$identifier);
98  $propertyManager = ‪MfaProviderPropertyManager::create($provider, $targetUser);
99  if (!$provider->isActive($propertyManager)) {
100  continue;
101  }
102  $activeProviders[‪$identifier] = $provider;
103  if ($provider->isLocked($propertyManager)) {
104  $lockedProviders[] = ‪$identifier;
105  }
106  }
107 
108  if ($activeProviders !== []) {
109  // Change status label to MFA being enabled
110  $status = '<span class="badge badge-success badge-space-end t3js-mfa-status-label"' . ' data-alternative-label="' . $disabledLabel . '">' . $enabledLabel . '</span>';
111 
112  // Add providers list
113  $childHtml[] = '<ul class="list-group t3js-mfa-active-providers-list">';
114  foreach ($activeProviders as ‪$identifier => $activeProvider) {
115  $childHtml[] = '<li class="list-group-item" id="provider-' . htmlspecialchars((string)‪$identifier) . '" style="line-height: 2.1em;">';
116  $childHtml[] = $this->iconFactory->getIcon($activeProvider->getIconIdentifier(), IconSize::SMALL);
117  $childHtml[] = htmlspecialchars($lang->sL($activeProvider->getTitle()));
118  if (in_array(‪$identifier, $lockedProviders, true)) {
119  $childHtml[] = '<span class="badge badge-danger">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.locked')) . '</span>';
120  } else {
121  $childHtml[] = '<span class="badge badge-success">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.active')) . '</span>';
122  }
123  if ($isDeactivationAllowed) {
124  $childHtml[] = '<button type="button"';
125  $childHtml[] = ' class="btn btn-default btn-sm float-end t3js-deactivate-provider-button"';
126  $childHtml[] = ' data-confirmation-title="' . htmlspecialchars(sprintf($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfaProvider'), $lang->sL($activeProvider->getTitle()))) . '"';
127  $childHtml[] = ' data-confirmation-content="' . htmlspecialchars(sprintf($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfaProvider.confirmation.text'), $lang->sL($activeProvider->getTitle()))) . '"';
128  $childHtml[] = ' data-confirmation-cancel-text="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel')) . '"';
129  $childHtml[] = ' data-confirmation-deactivate-text="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.deactivate')) . '"';
130  $childHtml[] = ' data-provider="' . htmlspecialchars((string)‪$identifier) . '"';
131  $childHtml[] = ' title="' . htmlspecialchars(sprintf($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfaProvider'), $lang->sL($activeProvider->getTitle()))) . '"';
132  $childHtml[] = '>';
133  $childHtml[] = $this->iconFactory->getIcon('actions-delete', IconSize::SMALL)->render('inline');
134  $childHtml[] = '</button>';
135  }
136  $childHtml[] = '</li>';
137  }
138  $childHtml[] = '</ul>';
139  }
140  }
141 
142  $fieldId = 't3js-form-field-mfa-id' . ‪StringUtility::getUniqueId('-');
143 
144  $html[] = '<div class="formengine-field-item t3js-formengine-field-item" id="' . htmlspecialchars($fieldId) . '">';
145  $html[] = '<div class="form-control-wrap" style="max-width: ' . $this->‪formMaxWidth($this->defaultInputWidth) . 'px">';
146  $html[] = '<div class="form-wizards-wrap">';
147  $html[] = '<div class="form-wizards-element">';
148  $html[] = implode(PHP_EOL, $childHtml);
149  if ($isDeactivationAllowed) {
150  $html[] = '<div class="form-wizards-items-bottom">';
151  $html[] = '<button type="button"';
152  $html[] = ' class="t3js-deactivate-mfa-button btn btn-danger ' . ($activeProviders === [] ? 'disabled" disabled="disabled' : '') . '"';
153  $html[] = ' data-confirmation-title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfa')) . '"';
154  $html[] = ' data-confirmation-content="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfa.confirmation.text')) . '"';
155  $html[] = ' data-confirmation-cancel-text="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel')) . '"';
156  $html[] = ' data-confirmation-deactivate-text="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.deactivate')) . '"';
157  $html[] = '>';
158  $html[] = $this->iconFactory->getIcon('actions-toggle-off', IconSize::SMALL)->render('inline');
159  $html[] = htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:buttons.deactivateMfa'));
160  $html[] = '</button>';
161  $html[] = '</div>';
162  }
163  $html[] = '</div>';
164  $html[] = '</div>';
165  $html[] = '</div>';
166  $html[] = '</div>';
167 
168  // JavaScript is not needed in case deactivation is not allowed or no active providers exist
169  if ($isDeactivationAllowed && $activeProviders !== []) {
170  $resultArray['javaScriptModules'][] = ‪JavaScriptModuleInstruction::create(
171  '@typo3/backend/form-engine/element/mfa-info-element.js'
172  )->instance('#' . $fieldId, ['userId' => $userId, 'tableName' => $tableName]);
173  }
174 
175  $resultArray['html'] = $this->‪wrapWithFieldsetAndLegend($status . implode(PHP_EOL, $html));
176  return $resultArray;
177  }
178 }
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\getBackendUser
‪getBackendUser()
Definition: AbstractFormElement.php:461
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\create
‪static create(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:47
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:37
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\wrapWithFieldsetAndLegend
‪wrapWithFieldsetAndLegend(string $innerHTML)
Definition: AbstractFormElement.php:133
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderPropertyManager\create
‪static create(MfaProviderManifestInterface $provider, AbstractUserAuthentication $user)
Definition: MfaProviderPropertyManager.php:193
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\getLanguageService
‪getLanguageService()
Definition: AbstractFormElement.php:456
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:61
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\formMaxWidth
‪int formMaxWidth($size=48)
Definition: AbstractFormElement.php:332
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderPropertyManager
Definition: MfaProviderPropertyManager.php:33
‪TYPO3\CMS\Backend\Form\Element\MfaInfoElement\ALLOWED_TABLES
‪const ALLOWED_TABLES
Definition: MfaInfoElement.php:38
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:33
‪TYPO3\CMS\Backend\Form\Element\MfaInfoElement\render
‪render()
Definition: MfaInfoElement.php:45
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Backend\Form\Element\MfaInfoElement\__construct
‪__construct(private readonly IconFactory $iconFactory, private readonly MfaProviderRegistry $mfaProviderRegistry,)
Definition: MfaInfoElement.php:40
‪TYPO3\CMS\Backend\Form\Element\MfaInfoElement
Definition: MfaInfoElement.php:37
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪initializeResultArray()
Definition: AbstractNode.php:77
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry
Definition: MfaProviderRegistry.php:28