From d3576c2dd7aaa1c7df4fae119865935e5d369371 Mon Sep 17 00:00:00 2001 From: Tobias Matthaiou Date: Tue, 13 Sep 2022 17:00:52 +0200 Subject: [PATCH] Require administrators to activate 2FA. --- .../Controller/Admin/d3force_2fa.php | 46 +++++ .../views/admin/de/d3totp_lang.php | 7 + .../views/admin/en/d3totp_lang.php | 7 + .../views/admin/tpl/d3user_totp.tpl | 39 ++++- src/Modules/Core/d3_totp_utils.php | 161 ++++++++++-------- src/metadata.php | 12 +- 6 files changed, 201 insertions(+), 71 deletions(-) create mode 100644 src/Application/Controller/Admin/d3force_2fa.php diff --git a/src/Application/Controller/Admin/d3force_2fa.php b/src/Application/Controller/Admin/d3force_2fa.php new file mode 100644 index 0000000..3ff40fe --- /dev/null +++ b/src/Application/Controller/Admin/d3force_2fa.php @@ -0,0 +1,46 @@ +addTplParam('force2FA', true); + + $userID = $this->d3GetSessionObject()->getVariable("auth"); + $this->_sEditObjectId = $userID; + + return parent::render(); + } + + + protected function _authorize() + { + $userID = $this->d3GetSessionObject()->getVariable("auth"); + + return ($this->d3IsAdminForce2FA() && !empty($userID)); + } + + /** + * @return Session + */ + private function d3GetSessionObject() + { + return Registry::getSession(); + } + + /** + * @return bool + */ + private function d3IsAdminForce2FA() + { + return $this->isAdmin() && + Registry::getConfig()->getConfigParam('D3_TOTP_ADMIN_FORCE_2FA') == true; + } +} diff --git a/src/Application/views/admin/de/d3totp_lang.php b/src/Application/views/admin/de/d3totp_lang.php index b905255..19ff622 100644 --- a/src/Application/views/admin/de/d3totp_lang.php +++ b/src/Application/views/admin/de/d3totp_lang.php @@ -34,6 +34,11 @@ $aLang = [ 'D3_TOTP_CURROTP' => 'Bestätigung mit Einmalpasswort', 'D3_TOTP_CURROTP_HELP' => 'Haben Sie dieses Kundenkonto in Ihrer Authentisierungs-App registriert, generieren Sie damit ein Einmalpasswort, tragen Sie es hier ein und senden das Formular direkt darauf hin ab.', + 'D3_TOTP_FORCE2FATITLE' => 'Verpflichtet Zwei-Faktor-Authentisierung', + 'D3_TOTP_FORCE2FASUB' => 'Alle Administratoren müssen es aktivieren', + 'D3_TOTP_ADMINBACKEND' => 'Admin-Oberfläche', + 'D3_TOTP_ADMINCONTINUE' => 'weiter', + 'D3_TOTP_REGISTEREXIST' => 'vorhandene Registrierung', 'D3_TOTP_REGISTERDELETE' => 'Registrierung löschen', 'D3_TOTP_REGISTERDELETE_DESC' => 'Um die Registrierung zu ändern, löschen Sie diese bitte vorerst. Sie können sofort im Anschluss eine neue Registrierung anlegen.
Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zwei-Faktor-Authentisierung geschützt.', @@ -48,4 +53,6 @@ $aLang = [ 'D3_TOTP_ERROR_UNVALID' => 'Das Einmalpasswort ist ungültig.', 'D3_TOTP_ALREADY_EXIST' => 'Die Registrierung wurde schon gespeichert.', + + 'SHOP_MODULE_D3_TOTP_ADMIN_FORCE_2FA' => 'Administratoren sind verpflichtet 2FA zu aktivieren' ]; diff --git a/src/Application/views/admin/en/d3totp_lang.php b/src/Application/views/admin/en/d3totp_lang.php index 77c90fa..2289325 100644 --- a/src/Application/views/admin/en/d3totp_lang.php +++ b/src/Application/views/admin/en/d3totp_lang.php @@ -34,6 +34,11 @@ $aLang = [ 'D3_TOTP_CURROTP' => 'Confirmation with one-time password', 'D3_TOTP_CURROTP_HELP' => 'If you have registered this customer account in your authentication app, you generate a one-time password, enter it here and send the form out immediately.', + 'D3_TOTP_FORCE2FATITLE' => 'Mandates two-factor authentication', + 'D3_TOTP_FORCE2FASUB' => 'All administrators need to activate it', + 'D3_TOTP_ADMINBACKEND' => 'Admin-Backend', + 'D3_TOTP_ADMINCONTINUE' => 'continue', + 'D3_TOTP_REGISTEREXIST' => 'existing registration', 'D3_TOTP_REGISTERDELETE' => 'Delete registration', 'D3_TOTP_REGISTERDELETE_DESC' => 'To change the registration, please delete it. You can then immediately create a new registration.
If you delete the registration, the account is no longer protected by the two-factor authentication.', @@ -48,4 +53,6 @@ $aLang = [ 'D3_TOTP_ERROR_UNVALID' => 'The one-time password is invalid.', 'D3_TOTP_ALREADY_EXIST' => 'The registration has already been saved.', + + 'SHOP_MODULE_D3_TOTP_ADMIN_FORCE_2FA' => 'Administrators are required to activate 2FA' ]; diff --git a/src/Application/views/admin/tpl/d3user_totp.tpl b/src/Application/views/admin/tpl/d3user_totp.tpl index 96f652a..e0e521d 100644 --- a/src/Application/views/admin/tpl/d3user_totp.tpl +++ b/src/Application/views/admin/tpl/d3user_totp.tpl @@ -14,8 +14,25 @@ td.edittext { white-space: normal; } + .hero { + display: inline-block; + } + .hero > h1 { + padding: 0.3em 0; + } + .hero > div { + text-align: right; + color: #6c7c98; + } +[{if $force2FA}] +
+

[{oxmultilang ident="D3_TOTP_FORCE2FATITLE"}]

+
[{oxmultilang ident="D3_TOTP_FORCE2FASUB"}]
+
+[{/if}] +
[{$oViewConf->getHiddenSid()}] @@ -60,6 +77,22 @@ [{oxinputhelp ident="D3_TOTP_QRCODE_HELP"}] + [{elseif $force2FA}] + + +

[{oxmultilang ident="D3_TOTP_ADMINBACKEND"}]

+ + + + + + + + [{else}] @@ -153,5 +186,7 @@ [{/if}]
-[{include file="bottomnaviitem.tpl"}] -[{include file="bottomitem.tpl"}] \ No newline at end of file +[{if !$force2FA}] + [{include file="bottomnaviitem.tpl"}] + [{include file="bottomitem.tpl"}] +[{/if}] diff --git a/src/Modules/Core/d3_totp_utils.php b/src/Modules/Core/d3_totp_utils.php index 7266357..60fc250 100644 --- a/src/Modules/Core/d3_totp_utils.php +++ b/src/Modules/Core/d3_totp_utils.php @@ -1,68 +1,93 @@ - - * @link http://www.oxidmodule.com - */ - -namespace D3\Totp\Modules\Core; - -use D3\Totp\Application\Model\d3totp; -use Doctrine\DBAL\DBALException; -use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; -use OxidEsales\Eshop\Core\Registry; -use OxidEsales\Eshop\Core\Session; - -class d3_totp_utils extends d3_totp_utils_parent -{ - /** - * @return bool - * @throws DBALException - * @throws DatabaseConnectionException - */ - public function checkAccessRights() - { - $blAuth = parent::checkAccessRights(); - - $userID = $this->d3GetSessionObject()->getVariable("auth"); - $totpAuth = (bool) $this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME); - /** @var d3totp $totp */ - $totp = $this->d3GetTotpObject(); - $totp->loadByUserId($userID); - - if ($blAuth && $totp->isActive() && false === $totpAuth) { - $this->redirect('index.php?cl=login', true, 302); - if (false == defined('OXID_PHP_UNIT')) { - // @codeCoverageIgnoreStart - exit; - // @codeCoverageIgnoreEnd - } - } - - return $blAuth; - } - - /** - * @return Session - */ - public function d3GetSessionObject() - { - return Registry::getSession(); - } - - /** - * @return d3totp - */ - public function d3GetTotpObject() - { - return oxNew(d3totp::class); - } -} \ No newline at end of file + + * @link http://www.oxidmodule.com + */ + +namespace D3\Totp\Modules\Core; + +use D3\Totp\Application\Model\d3totp; +use Doctrine\DBAL\DBALException; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Session; + +class d3_totp_utils extends d3_totp_utils_parent +{ + /** + * @return bool + * @throws DBALException + * @throws DatabaseConnectionException + */ + public function checkAccessRights() + { + $blAuth = parent::checkAccessRights(); + + $userID = $this->d3GetSessionObject()->getVariable("auth"); + $totpAuth = (bool) $this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME); + /** @var d3totp $totp */ + $totp = $this->d3GetTotpObject(); + $totp->loadByUserId($userID); + + //checkt ob alle Admin 2FA aktiviert hat + //todo braucht Unit Test + if ( + $this->d3IsAdminForce2FA() + && $blAuth + && $totp->isActive() === false + ) { + $this->redirect('index.php?cl=d3force_2fa', true, 302); + if (false == defined('OXID_PHP_UNIT')) { + // @codeCoverageIgnoreStart + exit; + // @codeCoverageIgnoreEnd + } + } + + //staten der prüfung vom einmalpasswort + if ($blAuth && $totp->isActive() && false === $totpAuth) { + $this->redirect('index.php?cl=login', true, 302); + if (false == defined('OXID_PHP_UNIT')) { + // @codeCoverageIgnoreStart + exit; + // @codeCoverageIgnoreEnd + } + } + + return $blAuth; + } + + /** + * @return Session + */ + public function d3GetSessionObject() + { + return Registry::getSession(); + } + + /** + * @return d3totp + */ + public function d3GetTotpObject() + { + return oxNew(d3totp::class); + } + + /** + * @return bool + */ + private function d3IsAdminForce2FA() + { + return $this->isAdmin() && + Registry::getConfig()->getConfigParam('D3_TOTP_ADMIN_FORCE_2FA') == true; + } +} diff --git a/src/metadata.php b/src/metadata.php index 3186684..594a0e8 100644 --- a/src/metadata.php +++ b/src/metadata.php @@ -16,6 +16,7 @@ */ use D3\Totp\Application\Controller\Admin\d3user_totp; +use D3\Totp\Application\Controller\Admin\d3force_2fa; use D3\Totp\Application\Controller\d3_account_totp; use D3\Totp\Application\Controller\d3totplogin; use D3\Totp\Modules\Application\Component\d3_totp_UserComponent; @@ -72,6 +73,7 @@ $aModule = [ ], 'controllers' => [ 'd3user_totp' => d3user_totp::class, + 'd3force_2fa' => d3force_2fa::class, 'd3totplogin' => d3totplogin::class, 'd3_account_totp' => d3_account_totp::class, ], @@ -80,6 +82,14 @@ $aModule = [ 'd3totplogin.tpl' => 'd3/totp/Application/views/tpl/d3totplogin.tpl', 'd3_account_totp.tpl' => 'd3/totp/Application/views/tpl/d3_account_totp.tpl', ], + 'settings' => [ + [ + 'group' => 'main', + 'name' => 'D3_TOTP_ADMIN_FORCE_2FA', + 'type' => 'bool', + 'value' => false, + ] + ], 'events' => [ 'onActivate' => '\D3\Totp\Setup\Events::onActivate', 'onDeactivate' => '\D3\Totp\Setup\Events::onDeactivate', @@ -94,6 +104,6 @@ $aModule = [ 'template' => 'page/account/inc/account_menu.tpl', 'block' => 'account_menu', 'file' => 'Application/views/blocks/page/account/inc/account_menu.tpl', - ] + ], ] ];