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
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ language: php
matrix:
fast_finish: true
include:
- name: 'PHP 7.1 | TYPO3 CMS v8'
php: '7.1'
env: TYPO3_VERSION=^8
- name: 'PHP 7.2 | TYPO3 CMS v9'
php: '7.2'
env: TYPO3_VERSION=^9
- name: 'PHP 7.2 | TYPO3 CMS v10'
php: '7.2'
env: TYPO3_VERSION=^10

sudo: false

Expand Down
225 changes: 225 additions & 0 deletions Classes/Backend/Form/Element/TwoFactorAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<?php

declare(strict_types=1);

/**
* Class UserSettings
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
* @link https://github.com/codeFareith/cf_google_authenticator
* @see https://www.fareith.de
* @see https://typo3.org
*/

namespace CodeFareith\CfGoogleAuthenticator\Backend\Form\Element;

use CodeFareith\CfGoogleAuthenticator\Domain\Immutable\AuthenticationSecret;
use CodeFareith\CfGoogleAuthenticator\Service\GoogleQrCodeGenerator;
use CodeFareith\CfGoogleAuthenticator\Service\QrCodeGeneratorInterface;
use CodeFareith\CfGoogleAuthenticator\Traits\GeneralUtilityObjectManager;
use CodeFareith\CfGoogleAuthenticator\Utility\Base32Utility;
use CodeFareith\CfGoogleAuthenticator\Utility\PathUtility;
use Exception;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
use TYPO3\CMS\Fluid\View\StandaloneView;
use function sprintf;
use function vsprintf;
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;

/**
* Custom field for the OTP setup in TCA
*
* This class hooks into the backend user settings,
* to extend the view by creating a secret key and an image of
* the QR code for the Google Authenticator.
*
* @package CodeFareith\CfGoogleAuthenticator\Hook
* @since 1.0.0
*/
class TwoFactorAuth extends AbstractFormElement
{
/*─────────────────────────────────────────────────────────────────────────────*\
Traits
\*─────────────────────────────────────────────────────────────────────────────*/
use GeneralUtilityObjectManager;

/*─────────────────────────────────────────────────────────────────────────────*\
Properties
\*─────────────────────────────────────────────────────────────────────────────*/
/**
* @var mixed[]
*/
protected $data;

/**
* @var AuthenticationSecret
*/
private $authenticationSecret;

/**
* @var QrCodeGeneratorInterface
*/
private $qrCodeGenerator;

/*─────────────────────────────────────────────────────────────────────────────*\
Methods
\*─────────────────────────────────────────────────────────────────────────────*/
/**
* @return array
* @throws Exception
*/
public function render(): array
{
$result = $this->initializeResultArray();
$authenticationSecret = $this->getAuthenticationSecret();
$templateView = $this->initializeTemplateView();
$isEnabled = $this->isGoogleAuthenticatorEnabled();
$qrCodeUri = $this->getQrCodeGenerator()->generateUri($authenticationSecret);

$prefix = '';
if ($this->data['tableName'] !== null) {
$prefix .= sprintf('[%s]', $this->data['tableName']);
}
if ($data['databaseRow']['uid'] !== null) {
$prefix .= sprintf('[%s]', (string)$this->data['databaseRow']['uid']);
}

$templateView->assignMultiple(
[
'prefix' => $prefix,
'isEnabled' => $isEnabled,
'qrCodeUri' => $qrCodeUri,
'authenticatorSecret' => $this->getAuthenticationSecret()->getSecretKey(),
]
);

$result['html'] = $templateView->render();

return $result;
}

private function initializeTemplateView(): StandaloneView
{
$templatePath = $this->getTemplatePath();

/** @var StandaloneView $templateView */
$templateView = $this->objectManager()->get(StandaloneView::class);
$templateView->setLayoutRootPaths([$templatePath . 'Layouts/']);
$templateView->setPartialRootPaths([$templatePath . 'Partials/']);
$templateView->setTemplateRootPaths([$templatePath . 'Templates/']);

$templateView->setTemplatePathAndFilename(
GeneralUtility::getFileAbsFileName(
PathUtility::makeExtensionPath('Resources/Private/Templates/Backend/UserSettings.html')
)
);

return $templateView;
}

private function getTemplatePath(): string
{
return GeneralUtility::getFileAbsFileName(
PathUtility::makeExtensionPath('Resources/Private/')
);
}

private function getIssuer(): string
{
return vsprintf(
'%s - %s',
[
$this->getSiteName(),
$this->getLayer(),
]
);
}

private function getSiteName(): string
{
return $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
}

private function getLayer(): string
{
$layer = '';

if ($this->data['tableName'] === 'fe_users') {
$layer = 'Frontend';
} elseif ($this->data['tableName'] === 'be_users') {
$layer = 'Backend';
}

$dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
$signalArguments = [
'table' => $this->data['tableName'],
'layer' => $layer,
'caller' => $this,
];
$signalArguments = $dispatcher->dispatch(
__CLASS__,
'defineIssuerLayer',
$signalArguments
);

return $signalArguments['layer'];
}

private function getUsername(): string
{
return $this->data['databaseRow']['username'] ?? '';
}

/**
* @throws Exception
*/
private function getAuthenticationSecret(): AuthenticationSecret
{
if ($this->authenticationSecret === null) {
$this->authenticationSecret = $this->objectManager()->get(
AuthenticationSecret::class,
$this->getIssuer(),
$this->getUsername(),
$this->getSecretKey()
);
}

return $this->authenticationSecret;
}

/**
* @throws Exception
*/
private function getSecretKey(): string
{
if ($this->isGoogleAuthenticatorEnabled()) {
$secretKey = (string) $this->data['databaseRow']['tx_cfgoogleauthenticator_secret'];
} else {
$secretKey = Base32Utility::generateRandomString(16);
}

return $secretKey;
}

private function isGoogleAuthenticatorEnabled(): bool
{
if ($this->data['parameterArray']['fieldConf']['config']['type'] === 'user' && !is_array($this->data['databaseRow'])) {
$this->data['databaseRow'] = $GLOBALS['BE_USER']->user;
}
return (bool) $this->data['databaseRow']['tx_cfgoogleauthenticator_enabled'];
}

private function getQrCodeGenerator(): QrCodeGeneratorInterface
{
if ($this->qrCodeGenerator === null) {
$this->qrCodeGenerator = $this->objectManager()->get(GoogleQrCodeGenerator::class);
}

return $this->qrCodeGenerator;
}
}
31 changes: 26 additions & 5 deletions Classes/Controller/Frontend/SetupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class SetupController
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down Expand Up @@ -33,7 +33,8 @@
use TYPO3\CMS\Extbase\Object\Exception as ObjectException;
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException;
use TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException;
use TYPO3\CMS\Lang\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
use function get_class;
use function vsprintf;

Expand Down Expand Up @@ -82,6 +83,11 @@ class SetupController
*/
private $authenticationSecret;

/**
* @var Dispatcher
*/
protected $dispatcher;

/*─────────────────────────────────────────────────────────────────────────────*\
Methods
\*─────────────────────────────────────────────────────────────────────────────*/
Expand All @@ -90,16 +96,16 @@ public function __construct(
GoogleQrCodeGenerator $qrCodeGenerator,
SetupFormValidator $setupFormValidator,
LanguageService $languageService,
Context $context
Context $context,
Dispatcher $dispatcher
)
{
parent::__construct();

$this->frontendUserRepository = $frontendUserRepository;
$this->qrCodeGenerator = $qrCodeGenerator;
$this->setupFormValidator = $setupFormValidator;
$this->languageService = $languageService;
$this->context = $context;
$this->dispatcher = $dispatcher;
}

/**
Expand Down Expand Up @@ -153,16 +159,31 @@ public function updateAction(): void
if ($user !== null) {
$formData = (array)$this->request->getArgument(SetupForm::FORM_NAME);

$action = null;
if ($this->request->hasArgument('enable')) {
$user->enableGoogleAuthenticator($formData['secret']);
$action = 'enable';
} elseif ($this->request->hasArgument('disable')) {
$user->disableGoogleAuthenticator();
$action = 'disable';
}

$this->frontendUserRepository->update($user);

$this->addSuccessMessage();

if ($action !== null) {
$this->dispatcher->dispatch(
__CLASS__,
'toggleGoogleAuthenticator',
[
'action' => $action,
'user' => $user,
'caller' => $this,
]
);
}

$this->redirect('index');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class GoogleAuthenticatorSettingsDTO
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class PreProcessFieldArrayDTO
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
2 changes: 1 addition & 1 deletion Classes/Domain/Form/FormInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Interface FormInterface
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
2 changes: 1 addition & 1 deletion Classes/Domain/Form/SetupForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class SetupForm
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
2 changes: 1 addition & 1 deletion Classes/Domain/Immutable/AuthenticationSecret.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class AuthenticationSecret
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
2 changes: 1 addition & 1 deletion Classes/Domain/Immutable/ImmutableInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Interface ImmutableInterface
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
2 changes: 1 addition & 1 deletion Classes/Domain/Mapper/AbstractMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class AbstractMapper
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Class GoogleAuthenticatorSettingsMapper
*
* @author Robin 'codeFareith' von den Bergen <robinvonberg@gmx.de>
* @copyright (c) 2018-2019 by Robin von den Bergen
* @copyright (c) 2018-2022 by Robin von den Bergen
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version 1.0.0
*
Expand Down
Loading