Compare commits

...

32 Commits

Author SHA1 Message Date
Daniel Seifert 957d1cfc7c
adjust version information 2023-02-18 21:58:07 +01:00
Daniel Seifert 24d1cd6688
adjust changelog 2023-01-26 00:14:28 +01:00
Daniel Seifert 5a69d23db2
rename package 2023-01-26 00:12:47 +01:00
Daniel Seifert 52d4b48849
make installable in OXID 6.5.1 (CE 6.13) 2022-12-13 14:30:51 +01:00
Daniel Seifert b9db9e601d
adjust mockable function calls, make compatible to same class extensions from Webauthn plugin 2022-12-07 12:06:24 +01:00
Daniel Seifert b18196613e
set current user to use it before session reload 2022-11-30 22:33:43 +01:00
Daniel Seifert 0dc6c49e0b
fix missing redirect on lost session while admin login 2022-11-26 00:23:04 +01:00
Daniel Seifert c13b1e04a0
update screenshots 2022-11-25 20:47:01 +01:00
Daniel Seifert f14ce58d6e
improve code syntax 2022-11-25 20:24:09 +01:00
Daniel Seifert a9a279a753
adjust tests 2022-11-25 20:11:04 +01:00
Daniel Seifert 7a2648fe7b
improve code 2022-11-25 15:42:33 +01:00
Daniel Seifert a3c75df635
adjust tests 2022-11-25 09:49:31 +01:00
Daniel Seifert a809c04b5b
set selected language for otp form 2022-11-24 20:27:07 +01:00
Daniel Seifert 77eca02079
change session admin auth variable to constant 2022-11-24 20:17:50 +01:00
Daniel Seifert 331a05b080
handle delete keys in OTP input form 2022-11-24 09:36:39 +01:00
Daniel Seifert c5d9fea2fe
remove required attribte from OTP input field to make 'cancel login' button clickable without an input 2022-11-24 00:53:30 +01:00
Daniel Seifert c86984df5f
move OTP check from login controller check to onAdminLoginEvent for webauthn compatibility 2022-11-24 00:51:56 +01:00
Daniel Seifert 749c654b4e
format otp input fields 2022-11-23 22:25:33 +01:00
Daniel Seifert 46ae7efa32
separate session var names between frontend and backend 2022-11-23 21:48:34 +01:00
Daniel Seifert d8be836ed7
adjust tests 2022-11-17 00:30:15 +01:00
Daniel Seifert d7912f6371
prevent type error 2022-11-14 09:07:34 +01:00
Daniel Seifert 4edfa2d3c3
use separated input fields for totp code in front- and backend 2022-11-14 08:55:37 +01:00
Daniel Seifert 12bb6355ee
fix unthrown invalid totp exception 2022-11-14 00:24:04 +01:00
Daniel Seifert 42bab2bd25
layout admin panel 2022-11-12 23:56:41 +01:00
Daniel Seifert e65c2e7acb
add missing tests 2022-11-12 22:31:19 +01:00
Daniel Seifert be69ed889e
layout admin panel 2022-11-12 00:52:10 +01:00
Daniel Seifert 222b7b345a
add 2FA links into account dashboard and the header menu 2022-11-12 00:25:46 +01:00
Daniel Seifert 3327fc9242
adjust tests 2022-11-11 23:57:29 +01:00
Daniel Seifert 02f2f6a843
fix handle session variables
* attempted login user id will stored in session while totp request only
* successful totp login stores user id in totp auth session variable
2022-11-10 11:34:05 +01:00
Daniel Seifert e3d2156d44
extract TOTP check from admin login 2022-11-10 00:55:19 +01:00
Daniel Seifert c80b5f626f
rename module methods in extended OXID classes to prevent conflicts with other modules, move totp check to _afterLogin for webauthn module compatibility 2022-11-09 12:03:16 +01:00
Daniel Seifert f110142474
rename module methods in extended OXID classes to prevent conflicts with other modules 2022-11-09 11:27:11 +01:00
61 changed files with 2466 additions and 1344 deletions

View File

@ -4,7 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://git.d3data.de/D3Public/oxtotp/compare/2.0.0.1...rel_2.x)
## [Unreleased](https://git.d3data.de/D3Public/oxtotp/compare/2.1.0.0...rel_2.x)
## [2.1.0.0](https://git.d3data.de/D3Public/oxtotp/compare/2.0.0.1...2.1.0.0) - 2023-02-18
- add compatibility to D3 Webauthn plugin
- installable in OXID 6.5.1
## [2.0.0.1](https://git.d3data.de/D3Public/oxtotp/compare/2.0.0.0...2.0.0.1) - 2022-11-09
### Fixed

View File

@ -1,9 +1,9 @@
[![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md)
[![english version](https://logos.oxidmodule.com/en2_xs.svg)](README.en.md)
# 2-factor authentication (TOTP) for OXID eShop
# 2-factor authentication (one-time password) for OXID eShop
This module provides a 2-factor authentication (time-dependent one-time password) for login in front- and backend in addition to user name and password.
This module provides a 2-factor authentication (time-dependent one-time password / TOTP) for login in front- and backend in addition to user name and password.
## Features
@ -20,8 +20,8 @@ This module provides a 2-factor authentication (time-dependent one-time password
![Setup Frontend](assets/setup_frontend.jpg "Setup Frontend")
### Login
![Login Backend](assets/login_backend.jpg "Login Backend")
![Login Frontend](assets/login_frontend.jpg "Login Frontend")
![Login Backend](assets/login_backend.png "Login Backend")
![Login Frontend](assets/login_frontend.png "Login Frontend")
## System requirements
@ -33,10 +33,14 @@ This package requires an OXID eShop installed with Composer in one of the follow
and its requirements.
The Flow and Wave themes are supported by default. Other themes may require customisation.
## Getting Started
Open a command line interface and navigate to the shop root directory (parent of source and vendor). Execute the following command. Adapt the paths to your environment.
```
composer require d3/oxtotp
composer require d3/oxid-twofactor-onetimepassword
```
Activate the module in the admin area of the shop in "Extensions -> Modules".

View File

@ -1,9 +1,9 @@
[![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md)
[![english version](https://logos.oxidmodule.com/en2_xs.svg)](README.en.md)
# 2-Faktor-Authentisierung (TOTP) fĂĽr OXID eShop
# 2-Faktor-Authentisierung (Einmalpasswort) fĂĽr OXID eShop
Dieses Modul stellt eine 2-Faktor-Authentisierung (zeitabhängiges Einmalpasswort) zum Login in Front- und Backend zusätzlich zu Benutzername und Passwort zur Verfügung.
Dieses Modul stellt eine 2-Faktor-Authentisierung (zeitabhängiges Einmalpasswort / TOTP) zum Login in Front- und Backend zusätzlich zu Benutzername und Passwort zur Verfügung.
## Features
@ -20,8 +20,8 @@ Dieses Modul stellt eine 2-Faktor-Authentisierung (zeitabhängiges Einmalpasswor
![Einrichtung Frontend](assets/setup_frontend.jpg "Einrichtung Frontend")
### Login
![Login Backend](assets/login_backend.jpg "Login Backend")
![Login Frontend](assets/login_frontend.jpg "Login Frontend")
![Login Backend](assets/login_backend.png "Login Backend")
![Login Frontend](assets/login_frontend.png "Login Frontend")
## Systemanforderungen
@ -33,10 +33,14 @@ Dieses Paket erfordert einen mit Composer installierten OXID eShop in einer der
und dessen Anforderungen.
Im Standard wird das Flow- und Wave-Theme unterstützt. Andere Themes können Anpassungen erfordern.
## Erste Schritte
Ă–ffnen Sie eine Kommandozeile und navigieren Sie zum Stammverzeichnis des Shops (Elternverzeichnis von source und vendor). FĂĽhren Sie den folgenden Befehl aus. Passen Sie die Pfadangaben an Ihre Installationsumgebung an.
```
composer require d3/oxtotp
composer require d3/oxid-twofactor-onetimepassword
```
Aktivieren Sie das Modul im Shopadmin unter "Erweiterungen -> Module".

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
assets/login_backend.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

BIN
assets/login_frontend.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,5 +1,5 @@
{
"name": "d3/oxtotp",
"name": "d3/oxid-twofactor-onetimepassword",
"description": "Two-factor authentication via time-based one-time password for OXID eSales shop",
"type": "oxideshop-module",
"keywords": [
@ -7,7 +7,13 @@
"modules",
"eShop",
"d3",
"2FA"
"2FA",
"two factor",
"second factor",
"TOTP",
"OTP",
"one-time password",
"authenticator"
],
"authors": [
{
@ -40,21 +46,28 @@
"php": ">=7.2",
"ext-xmlwriter": "*",
"ext-openssl": "*",
"oxid-esales/oxideshop-ce": "6.8.0 - 6.12",
"oxid-esales/oxideshop-ce": "6.8.0 - 6.13",
"spomky-labs/otphp": "^10.0 || ^11.0",
"bacon/bacon-qr-code": "^2.0",
"laminas/laminas-math": "^3.2"
"laminas/laminas-math": "^3.2",
"d3/testingtools": "^1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.19",
"phpstan/phpstan": "^1.8"
},
"suggest": {
"d3/modcfg": "Provides automatic installation routines"
},
"autoload": {
"psr-4": {
"D3\\Totp\\": "../../../source/modules/d3/totp"
}
},
"scripts": {
"totp_phpstan": "./vendor/bin/phpstan -c./vendor/d3/oxtotp/phpstan.neon"
},
"suggest": {
"d3/oxid-twofactor-passwordless": "Passwordless login with FIDO2 hardware token."
},
"replace": {
"d3/oxtotp": "*"
}
}

View File

@ -5,4 +5,10 @@ parameters:
paths:
- src
level: 5
phpVersion: 70300
phpVersion: 70300
ignoreErrors:
- '#Call to method getFieldData\(\) on an unknown class oxShop.#'
- '#Return type \(array\) of method D3\\Totp\\Application\\Controller\\d3totplogin::getBreadCrumb\(\) should be compatible with return type \(null\) of method OxidEsales\\EshopCommunity\\Application\\Controller\\FrontendController::getBreadCrumb\(\)#'
- '#Parameter \#\d+ \$value of method OxidEsales\\EshopCommunity\\Core\\Config::setConfigParam\(\) expects string, (true|false) given.#'
parallel:
processTimeout: 900.0

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller\Admin;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
@ -13,7 +14,7 @@ class d3force_2fa extends d3user_totp
{
$this->addTplParam('force2FA', true);
$userID = $this->d3GetSessionObject()->getVariable("auth");
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$this->_sEditObjectId = $userID;
return parent::render();
@ -22,7 +23,7 @@ class d3force_2fa extends d3user_totp
protected function _authorize()
{
$userID = $this->d3GetSessionObject()->getVariable("auth");
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
return ($this->d3IsAdminForce2FA() && !empty($userID));
}
@ -30,7 +31,7 @@ class d3force_2fa extends d3user_totp
/**
* @return Session
*/
private function d3GetSessionObject()
private function d3TotpGetSessionObject()
{
return Registry::getSession();
}

View File

@ -0,0 +1,234 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Application\Controller\Admin;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use OxidEsales\Eshop\Application\Controller\Admin\AdminController;
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
use Psr\Log\LoggerInterface;
class d3totpadminlogin extends AdminController
{
protected $_sThisTemplate = 'd3totpadminlogin.tpl';
/**
* @return bool
*/
protected function _authorize(): bool
{
return true;
}
/**
* @return d3totp|mixed
*/
public function d3TotpGetTotpObject()
{
return oxNew(d3totp::class);
}
/**
* @return bool
* @throws DatabaseConnectionException
*/
protected function isTotpIsNotRequired(): bool
{
/** @var d3_totp_user $user */
$user = $this->d3TotpGetUserObject();
$userId = $user->d3TotpGetCurrentUser();
$totp = $this->d3TotpGetTotpObject();
$totp->loadByUserId($userId);
return $this->d3TotpGetSession()->hasVariable(d3totp_conf::SESSION_ADMIN_AUTH) ||
!$totp->isActive();
}
/**
* @return bool
*/
protected function isTotpLoginNotPossible(): bool
{
$user = $this->d3TotpGetUserObject();
return !$user->d3TotpGetCurrentUser();
}
/**
* @return string
* @throws DatabaseConnectionException
*/
public function render(): string
{
if ($this->isTotpLoginNotPossible()) {
$this->d3TotpGetUtils()->redirect('index.php?cl=login', false);
} elseif ($this->isTotpIsNotRequired()) {
$this->d3TotpGetUtils()->redirect('index.php?cl=admin_start', false);
}
$this->addTplParam('selectedProfile', Registry::getRequest()->getRequestEscapedParameter('profile'));
$this->addTplParam('selectedChLanguage', Registry::getRequest()->getRequestEscapedParameter('chlanguage'));
/** @var d3_totp_LoginController $loginController */
$loginController = $this->d3GetLoginController();
$loginController->d3totpAfterLoginSetLanguage();
return parent::render();
}
/**
* @return d3backupcodelist
*/
public function d3GetBackupCodeListObject(): d3backupcodelist
{
return oxNew(d3backupcodelist::class);
}
/**
* @return string|void
* @throws DatabaseConnectionException
*/
public function getBackupCodeCountMessage()
{
/** @var d3_totp_user $user */
$user = oxNew(User::class);
$userId = $user->d3TotpGetCurrentUser();
$oBackupCodeList = $this->d3GetBackupCodeListObject();
$iCount = $oBackupCodeList->getAvailableCodeCount($userId);
if ($iCount < 4) {
return sprintf(
Registry::getLang()->translateString('D3_TOTP_AVAILBACKUPCODECOUNT'),
$iCount
);
}
}
/**
* @return string
*/
public function d3CancelLogin(): string
{
/** @var d3_totp_user $oUser */
$oUser = $this->d3TotpGetUserObject();
$oUser->logout();
return "login";
}
/**
* @return User
*/
public function d3TotpGetUserObject(): User
{
return oxNew(User::class);
}
/**
* @return string|void
* @throws DatabaseConnectionException
*/
public function checklogin()
{
$session = $this->d3TotpGetSession();
/** @var d3_totp_user $user */
$user = oxNew(User::class);
$userId = $user->d3TotpGetCurrentUser();
try {
$sTotp = implode('', Registry::getRequest()->getRequestEscapedParameter('d3totp') ?: []);
$totp = $this->d3TotpGetTotpObject();
$totp->loadByUserId($userId);
$this->d3TotpHasValidTotp($sTotp, $totp);
$selectedProfile = Registry::getRequest()->getRequestEscapedParameter('profile');
$selectedLanguage = Registry::getRequest()->getRequestEscapedParameter('chlanguage');
$session->initNewSession();
$session->setVariable(d3totp_conf::SESSION_ADMIN_PROFILE, $selectedProfile);
$session->setVariable(d3totp_conf::SESSION_ADMIN_CHLANGUAGE, $selectedLanguage);
$session->setVariable(d3totp_conf::OXID_ADMIN_AUTH, $userId);
$session->setVariable(d3totp_conf::SESSION_ADMIN_AUTH, $userId);
$session->deleteVariable(d3totp_conf::SESSION_ADMIN_CURRENTUSER);
/** @var d3_totp_LoginController $loginController */
$loginController = $this->d3GetLoginController();
$loginController->d3totpAfterLogin();
return "admin_start";
} catch (d3totp_wrongOtpException $e) {
Registry::getUtilsView()->addErrorToDisplay($e);
$this->getLogger()->error($e->getMessage(), ['UserId' => $userId]);
$this->getLogger()->debug($e->getTraceAsString());
}
}
/**
* @param string|null $sTotp
* @param d3totp $totp
* @return bool
* @throws DatabaseConnectionException
* @throws d3totp_wrongOtpException
*/
public function d3TotpHasValidTotp(string $sTotp = null, d3totp $totp): bool
{
return $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH)
|| $totp->verify($sTotp);
}
/**
* @return Utils
*/
public function d3TotpGetUtils(): Utils
{
return Registry::getUtils();
}
/**
* @return Session
*/
public function d3TotpGetSession(): Session
{
return Registry::getSession();
}
/**
* @return LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return Registry::getLogger();
}
/**
* @return LoginController
*/
public function d3GetLoginController(): LoginController
{
return oxNew(LoginController::class);
}
}

View File

@ -35,12 +35,6 @@ class d3_account_totp extends AccountController
{
$sRet = parent::render();
/** @var User|null $oUser */
$oUser = $this->getUser();
if (false === $oUser instanceof User) {
return $this->_sThisTemplate = $this->_sThisLoginTemplate;
}
$this->addTplParam('user', $this->getUser());
return $sRet;
@ -114,7 +108,7 @@ class d3_account_totp extends AccountController
$oTotp->save();
$oTotpBackupCodes->save();
} catch (Exception $oExcp) {
Registry::get(UtilsView::class)->addErrorToDisplay($oExcp);
Registry::get(UtilsView::class)->addErrorToDisplay($oExcp->getMessage());
}
}
}

View File

@ -16,7 +16,7 @@ declare(strict_types=1);
namespace D3\Totp\Application\Controller;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
@ -28,18 +28,11 @@ class d3totplogin extends FrontendController
public function render()
{
if (Registry::getSession()->hasVariable(d3totp::TOTP_SESSION_VARNAME) ||
false == Registry::getSession()->hasVariable(d3totp::TOTP_SESSION_CURRENTUSER)
) {
$this->getUtils()->redirect('index.php?cl=start');
if (false == defined('OXID_PHP_UNIT')) {
// @codeCoverageIgnoreStart
exit;
// @codeCoverageIgnoreEnd
}
if (!Registry::getSession()->hasVariable(d3totp_conf::SESSION_CURRENTUSER)) {
$this->getUtils()->redirect('index.php?cl=start', false);
}
$this->addTplParam('navFormParams', Registry::getSession()->getVariable(d3totp::TOTP_SESSION_NAVFORMPARAMS));
$this->addTplParam('navFormParams', Registry::getSession()->getVariable(d3totp_conf::SESSION_NAVFORMPARAMS));
return parent::render();
}
@ -59,7 +52,8 @@ class d3totplogin extends FrontendController
public function getBackupCodeCountMessage()
{
$oBackupCodeList = $this->getBackupCodeListObject();
$iCount = $oBackupCodeList->getAvailableCodeCount(Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER));
$userId = Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER);
$iCount = $oBackupCodeList->getAvailableCodeCount($userId);
if ($iCount < 4) {
return sprintf(
@ -79,12 +73,12 @@ class d3totplogin extends FrontendController
public function getPreviousClass()
{
return Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTCLASS);
return Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTCLASS);
}
public function previousClassIsOrderStep(): bool
{
$sClassKey = Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTCLASS);
$sClassKey = Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTCLASS);
$resolvedClass = Registry::getControllerClassNameResolver()->getClassNameById($sClassKey);
$resolvedClass = $resolvedClass ?: 'start';
@ -106,7 +100,7 @@ class d3totplogin extends FrontendController
*
* @return array
*/
public function getBreadCrumb(): array
public function getBreadCrumb()
{
$aPaths = [];
$aPath = [];

View File

@ -15,11 +15,11 @@ declare(strict_types=1);
namespace D3\Totp\Application\Model;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
class d3backupcode extends BaseModel
{
@ -57,7 +57,7 @@ class d3backupcode extends BaseModel
public function d3EncodeBC($code, $sUserId)
{
$oDb = DatabaseProvider::getDb();
$oUser = $this->d3GetUserObject();
$oUser = $this->d3TotpGetUserObject();
$oUser->load($sUserId);
$salt = $oUser->getFieldData('oxpasssalt');
$sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )";
@ -74,8 +74,9 @@ class d3backupcode extends BaseModel
return $this->getUser();
}
$sUserId = Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER);
/** @var d3_totp_user $oUser */
$oUser = oxNew(User::class);
$sUserId = $oUser->d3TotpGetCurrentUser();
$oUser->load($sUserId);
return $oUser;
}
@ -83,7 +84,7 @@ class d3backupcode extends BaseModel
/**
* @return User
*/
public function d3GetUserObject()
public function d3TotpGetUserObject()
{
return oxNew(User::class);
}

View File

@ -29,11 +29,6 @@ use OxidEsales\Eshop\Core\Registry;
class d3totp extends BaseModel
{
public const TOTP_SESSION_VARNAME = 'totp_auth';
public const TOTP_SESSION_CURRENTUSER = 'd3totpCurrentUser';
public const TOTP_SESSION_CURRENTCLASS = 'd3totpCurrentClass';
public const TOTP_SESSION_NAVFORMPARAMS = 'd3totpNavFormParams';
public $tableName = 'd3totp';
public $userId;
public $totp;
@ -148,7 +143,7 @@ class d3totp extends BaseModel
{
if (false == $this->totp) {
$this->totp = TOTP::create($seed ?: $this->getSavedSecret());
$this->totp->setLabel($this->getUser()->getFieldData('oxusername'));
$this->totp->setLabel($this->getUser()->getFieldData('oxusername') ?: '');
$this->totp->setIssuer(Registry::getConfig()->getActiveShop()->getFieldData('oxname'));
}
@ -273,7 +268,7 @@ class d3totp extends BaseModel
}
/**
* @param null $oxid
* @param null|string $oxid
* @return bool
* @throws DatabaseConnectionException
*/

View File

@ -0,0 +1,30 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Application\Model;
class d3totp_conf
{
public const OXID_ADMIN_AUTH = 'auth';
public const OXID_FRONTEND_AUTH = 'usr';
public const SESSION_AUTH = 'd3Totp_auth'; // has valid totp, user is logged in completly
public const SESSION_CURRENTUSER = 'd3Totp_currentUser'; // oxid assigned to user from entered username
public const SESSION_CURRENTCLASS = 'd3Totp_currentClass'; // oxid assigned to user from entered username
public const SESSION_NAVFORMPARAMS = 'd3Totp_navFormParams';
public const SESSION_ADMIN_AUTH = 'd3Totp_auth'; // has valid totp, user is logged in completly
public const SESSION_ADMIN_CURRENTUSER = 'd3Totp_currentUser'; // oxid assigned to user from entered username
public const SESSION_ADMIN_PROFILE = 'd3Totp_currentProfile'; // selected profile
public const SESSION_ADMIN_CHLANGUAGE = 'd3Totp_currentChLanguage'; // selected language
}

View File

@ -25,6 +25,7 @@ $aLang = [
'D3_TOTP_BREADCRUMB' => 'Einmalpasswort-Anmeldung',
'D3_TOTP_ERROR_UNVALID' => 'Das Einmalpasswort ist ungĂĽltig.',
'D3_TOTP_ACCOUNT' => '2-Faktor-Authentisierung',
'D3_TOTP_ACCOUNT_DESC' => 'Sichern Sie Ihre Kontoanmeldung mit einem zweiten Faktor.',
'D3_TOTP_ACCOUNT_USE' => '2-Faktor-Authentisierung verwenden',

View File

@ -25,6 +25,7 @@ $aLang = [
'D3_TOTP_BREADCRUMB' => 'one-time password login',
'D3_TOTP_ERROR_UNVALID' => 'The one-time password is invalid.',
'D3_TOTP_ACCOUNT' => '2-factor authentication',
'D3_TOTP_ACCOUNT_DESC' => 'Secure your account login with a second factor.',
'D3_TOTP_ACCOUNT_USE' => 'Use 2-factor authentication',

View File

@ -41,6 +41,7 @@ $aLang = [
'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.<br>Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zwei-Faktor-Authentisierung geschützt.',
'D3_TOTP_REGISTERDELETED' => 'Die Registrierung wurde gelöscht.',
'D3_TOTP_CONFIRMATION' => 'Bestätigung',
'D3_TOTP_BACKUPCODES' => 'Backupcodes',
'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backupcodes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Authentisierung ändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.',
'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcode(s) verfĂĽgbar',

View File

@ -41,6 +41,7 @@ $aLang = [
'D3_TOTP_REGISTERDELETE_DESC' => 'To change the registration, please delete it. You can then immediately create a new registration. <br> If you delete the registration, the account is no longer protected by the two-factor authentication.',
'D3_TOTP_REGISTERDELETED' => 'The registration has been deleted.',
'D3_TOTP_CONFIRMATION' => 'confirmation',
'D3_TOTP_BACKUPCODES' => 'backup codes',
'D3_TOTP_BACKUPCODES_DESC' => 'You can use these backup codes to log on if it is not possible to generate the one-time password (e.g. device lost or newly installed). You can then change the settings to use 2-factor authentication or create a new 2FA login. Please save these codes safely at this moment. After leaving this page, these codes cannot be displayed again.',
'D3_TOTP_AVAILBACKUPCODECOUNT' => '%1$s backup code(s) still available',

View File

@ -0,0 +1,126 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>[{oxmultilang ident="LOGIN_TITLE"}]</title>
<meta http-equiv="Content-Type" content="text/html; charset=[{$charset}]">
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
<link rel="shortcut icon" href="[{$oViewConf->getImageUrl()}]favicon.ico">
<link rel="stylesheet" href="[{$oViewConf->getResourceUrl()}]login.css">
<link rel="stylesheet" href="[{$oViewConf->getResourceUrl()}]colors_[{$oViewConf->getEdition()|lower}].css">
</head>
<body>
<div class="admin-login-box">
<div id="shopLogo"><img src="[{$oViewConf->getImageUrl('logo_dark.svg')}]" alt="" /></div>
<form action="[{$oViewConf->getSelfLink()}]" method="post" id="login">
[{block name="admin_login_form"}]
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="profile" value="[{$selectedProfile}]">
<input type="hidden" name="chlanguage" value="[{$selectedChLanguage}]">
<h3>[{oxmultilang ident="TOTP_INPUT"}]</h3>
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
[{$oView->getBackupCodeCountMessage()}]
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fĂĽnfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
[{capture name="d3js"}]
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type === "text") {
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
[{/capture}]
[{oxscript add=$smarty.capture.d3js}]
<div>[{oxmultilang ident="TOTP_INPUT_HELP"}]</div>
<input type="submit" value="[{oxmultilang ident="LOGIN_START"}]" class="btn"><br>
<input class="btn btn_cancel" value="[{oxmultilang ident="TOTP_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3CancelLogin'; document.getElementById('login').submit();"
>
[{oxstyle include=$oViewConf->getModuleUrl('d3totp', 'out/admin/src/css/d3totplogin.css')}]
[{oxstyle}]
[{**
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="">
<input type="hidden" name="cl" value="login">
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
<div class="d3webauthn_icon">
<div class="svg-container">
[{include file=$oViewConf->getModulePath('d3webauthn', 'out/img/fingerprint.svg')}]
</div>
<div class="message">[{oxmultilang ident="WEBAUTHN_INPUT_HELP"}]</div>
</div>
**}]
[{* prevent cancel button (1st button) action when form is sent via Enter key *}]
[{**
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="[{oxmultilang ident="WEBAUTHN_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3WebauthnCancelLogin'; document.getElementById('login').submit();"
>
[{oxstyle include=$oViewConf->getModuleUrl('d3webauthn', 'out/admin/src/css/d3webauthnlogin.css')}]
[{oxstyle}]
**}]
[{/block}]
</form>
</div>
[{oxscript}]
<script type="text/javascript">if (window !== window.top) top.location.href = document.location.href;</script>
</body>
</html>

View File

@ -1,5 +1,12 @@
[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign}]
[{oxstyle include="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"}]
[{oxscript include="https://code.jquery.com/jquery-3.2.1.slim.min.js"}]
[{oxscript include="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"}]
[{oxscript include="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"}]
[{oxstyle include="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/solid.min.css"}]
[{oxstyle}]
[{assign var="totp" value=$edit->d3GetTotp()}]
[{assign var="userid" value=$edit->getId()}]
[{$totp->loadByUserId($userid)}]
@ -24,6 +31,9 @@
text-align: right;
color: #6c7c98;
}
.container-fluid {
font-size: 13px;
}
</style>
[{if $force2FA}]
@ -57,132 +67,124 @@
[{/if}]
[{if $oxid && $oxid != '-1'}]
<table style="padding:0; border:0; width:98%;">
<tr>
<td class="edittext" style="vertical-align: top; padding-top:10px;padding-left:10px; width: 50%;">
<table style="padding:0; border:0">
<div class="container-fluid">
<div class="row">
<div class="col-4">
<div class="card">
[{block name="user_d3user_totp_form1"}]
[{if false == $totp->getId()}]
<tr>
<td class="edittext" colspan="2">
<h4>[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]</h4>
</td>
</tr>
<tr>
<td class="edittext">
[{oxmultilang ident="D3_TOTP_QRCODE"}]&nbsp;
</td>
<td class="edittext">
[{$totp->getQrCodeElement()}]
[{oxinputhelp ident="D3_TOTP_QRCODE_HELP"}]
</td>
</tr>
<div class="card-header">
[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
[{oxmultilang ident="D3_TOTP_QRCODE" suffix="COLON"}]
</div>
<div class="col-8">
[{$totp->getQrCodeElement()}]
[{oxinputhelp ident="D3_TOTP_QRCODE_HELP"}]
</div>
</div>
</div>
[{elseif $force2FA}]
<tr>
<td class="edittext" colspan="2">
<h4>[{oxmultilang ident="D3_TOTP_ADMINBACKEND"}]</h4>
</td>
</tr>
<tr>
<td class="edittext" colspan="2">
<input
type="submit" class="edittext" id="oLockButton" name="delete"
value="[{oxmultilang ident="D3_TOTP_ADMINCONTINUE"}]"
onClick="document.myedit.fnc.value='';document.myedit.cl.value='admin_start'"
>
</td>
</tr>
<div class="card-header">
[{oxmultilang ident="D3_TOTP_ADMINBACKEND"}]
</div>
<div class="card-body">
<input
type="submit" class="edittext" id="oLockButton" name="delete"
value="[{oxmultilang ident="D3_TOTP_ADMINCONTINUE"}]"
onClick="document.myedit.fnc.value='';document.myedit.cl.value='admin_start'"
>
</div>
[{else}]
<tr>
<td class="edittext" colspan="2">
<h4>[{oxmultilang ident="D3_TOTP_REGISTEREXIST"}]</h4>
</td>
</tr>
<tr>
<td class="edittext" colspan="2">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE_DESC"}]
</td>
</tr>
<tr>
<td class="edittext" colspan="2"><br><br>
<input type="submit" class="edittext" id="oLockButton" name="delete" value="[{oxmultilang ident="D3_TOTP_REGISTERDELETE"}]" onClick="document.myedit.fnc.value='delete'" [{$readonly}]>
</td>
</tr>
<div class="card-header">
[{oxmultilang ident="D3_TOTP_REGISTEREXIST"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE_DESC"}]
<br>
<br>
<button type="submit" [{$readonly}] class="btn btn-primary btn-outline-danger btn-sm" onClick="document.myedit.fnc.value='delete'">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE"}]
</button>
</div>
</div>
<br>
</div>
[{/if}]
[{/block}]
</table>
</td>
<!-- Anfang rechte Seite -->
<td class="edittext" style="text-align: left; vertical-align: top; height:99%;padding-left:5px;padding-bottom:30px;padding-top:10px; width: 50%;">
<table style="padding:0; border:0">
</div>
</div>
<div class="col-8">
<div class="card">
[{block name="user_d3user_totp_form2"}]
[{if false == $totp->getId()}]
<tr>
<td class="edittext" colspan="2">
<h4>&nbsp;</h4>
</td>
</tr>
<tr>
<td class="edittext">
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET"}]</label>
</td>
<td class="edittext">
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
[{oxinputhelp ident="D3_TOTP_SECRET_HELP"}]
</td>
</tr>
<tr>
<td class="edittext">
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
</td>
<td class="edittext">
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" [{$readonly}]>
[{oxinputhelp ident="D3_TOTP_CURROTP_HELP"}]
</td>
</tr>
<tr>
<td class="edittext" colspan="2"><br><br>
<input type="submit" class="edittext" id="oLockButton" name="save" value="[{oxmultilang ident="D3_TOTP_SAVE"}]" onClick="document.myedit.fnc.value='save'" [{$readonly}]>
</td>
</tr>
<div class="card-header">
[{oxmultilang ident="D3_TOTP_CONFIRMATION"}]
</div>
<div class="card-body">
<div class="row">
<div class="col-4">
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET" suffix="COLON"}]</label>
</div>
<div class="col-8">
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
[{oxinputhelp ident="D3_TOTP_SECRET_HELP"}]
</div>
</div>
<div class="row" style="margin-top: 20px;">
<div class="col-4">
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
</div>
<div class="col-8">
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" autofocus="autofocus" [{$readonly}]>
[{oxinputhelp ident="D3_TOTP_CURROTP_HELP"}]
</div>
</div>
<div class="row">
<div class="col-4"></div>
<div class="col-8">
<button type="submit" [{$readonly}] class="btn btn-primary btn-success btn-sm" onClick="document.myedit.fnc.value='save'">
[{oxmultilang ident="D3_TOTP_SAVE"}]
</button>
</div>
</div>
</div>
[{else}]
<tr>
<td class="edittext" colspan="2">
<h4>[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]</h4>
</td>
</tr>
[{if $oView->getBackupCodes()}]
<tr>
<td>
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
<br>
<br>
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
</td>
</tr>
[{else}]
<tr>
<td>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT" args=$oView->getAvailableBackupCodeCount()}]
</td>
</tr>
<tr>
<td>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT_DESC"}]
</td>
</tr>
[{/if}]
<div class="card-header">
[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]
</div>
<div class="card-body">
[{if $oView->getBackupCodes()}]
<div class="row">
<div class="col-6">
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
</div>
<div class="col-6">
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
</div>
</div>
[{else}]
<div class="row">
<div class="col-12">
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT" args=$oView->getAvailableBackupCodeCount()}]
<br>
<br>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT_DESC"}]
</div>
</div>
[{/if}]
</div>
[{/if}]
[{/block}]
</table>
</td>
<!-- Ende rechte Seite -->
</tr>
</table>
</div>
</div>
</div>
</div>
[{/if}]
</form>

View File

@ -0,0 +1,11 @@
[{$smarty.block.parent}]
<div class="panel panel-default">
<div class="panel-heading">
<a href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]" class="btn btn-default btn-xs pull-right">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="panel-body">[{oxmultilang ident="D3_TOTP_ACCOUNT_DESC"}]</div>
</div>

View File

@ -0,0 +1,11 @@
[{$smarty.block.parent}]
<div class="card">
<div class="card-header">
<a href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]" class="btn btn-outline-dark btn-sm float-right edit-button">
<i class="fa fa-arrow-right"></i>
</a>
</div>
<div class="card-body">[{oxmultilang ident="D3_TOTP_ACCOUNT_DESC"}]</div>
</div>

View File

@ -0,0 +1,4 @@
[{$smarty.block.parent}]
<li>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_totp"}]">[{oxmultilang ident="D3_TOTP_ACCOUNT"}]</a>
</li>

View File

@ -11,20 +11,56 @@
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checkTotplogin">
<input type="hidden" name="fnc" value="d3TotpCheckTotpLogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]
<h3>[{oxmultilang ident="D3_TOTP_INPUT"}]</h3>
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
[{$oView->getBackupCodeCountMessage()}]
<label for="d3totp">[{oxmultilang ident="D3_TOTP_INPUT"}]</label>
<input type="text" name="d3totp" id="d3totp" value="" size="49" autofocus autocomplete="off"><br>
<div class="container">
<label for="1st">erste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id='1st' inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent(null, '2nd')" autofocus autocomplete="off">
<label for="2nd">zweite TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="2nd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('1st', '3rd')" autocomplete="off">
<label for="3rd">dritte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="3rd" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('2nd', '4th')" autocomplete="off">
<label for="4th">vierte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="4th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('3rd', '5th')" autocomplete="off">
<label for="5th">fĂĽnfte TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="5th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('4th', '6th')" autocomplete="off">
<label for="6th">sechste TOTP-Ziffer</label>
<input type="text" name="d3totp[]" class="digit" id="6th" inputmode="numeric" pattern="[0-9]*" maxlength="1" onkeyup="clickEvent('5th', null)" autocomplete="off">
</div>
[{oxmultilang ident="D3_TOTP_INPUT_HELP"}]
[{capture name="d3js"}]
function clickEvent(previous, next){
const digitKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const deleteKeys = ['Backspace', 'Delete'];
if(next && digitKeys.includes(event.key)){
document.getElementById(next).focus();
} else if(previous && deleteKeys.includes(event.key)){
document.getElementById(previous).focus();
}
}
document.addEventListener("paste", function(e) {
if (e.target.type === "text") {
var data = e.clipboardData.getData('Text');
data = data.split('');
[].forEach.call(document.querySelectorAll("#login input[type=text]"), (node, index) => {
node.value = data[index];
});
}
});
[{/capture}]
[{oxscript add=$smarty.capture.d3js}]
<div>[{oxmultilang ident="D3_TOTP_INPUT_HELP"}]</div>
<button type="submit" class="btn btn-primary">
[{oxmultilang ident="D3_TOTP_SUBMIT_LOGIN"}]
@ -34,7 +70,7 @@
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="cancelTotplogin">
<input type="hidden" name="fnc" value="d3TotpCancelTotpLogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]

View File

@ -11,6 +11,8 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Modules\Application\Component
{
@ -65,8 +67,13 @@ namespace D3\Totp\Modules\Core
{
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\EshopCommunity\Core\SystemEventHandler;
class d3_totp_utils_parent extends Utils
{
}
class totpSystemEventHandler_parent extends SystemEventHandler
{
}
}

View File

@ -16,8 +16,11 @@ declare(strict_types=1);
namespace D3\Totp\Modules\Application\Component;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use Doctrine\DBAL\DBALException;
use InvalidArgumentException;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
@ -28,39 +31,43 @@ use OxidEsales\Eshop\Core\UtilsView;
class d3_totp_UserComponent extends d3_totp_UserComponent_parent
{
/**
* @return void
* @param User $oUser
*
* @return string
* @throws DatabaseConnectionException
*/
public function login()
protected function _afterLogin($oUser)
{
parent::login();
if (!$oUser instanceof User) {
throw oxNew(InvalidArgumentException::class, 'user argument must an instance of User class');
}
$oUser = $this->getUser();
if ($oUser instanceof User && $oUser->getId()) {
if ($oUser->getId()) {
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($oUser->getId());
if ($totp->isActive()
&& !$this->d3GetSession()->getVariable(d3totp::TOTP_SESSION_VARNAME)
&& !$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_AUTH)
) {
$this->d3GetSession()->setVariable(
d3totp::TOTP_SESSION_CURRENTCLASS,
$this->d3TotpGetSession()->setVariable(
d3totp_conf::SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3totplogin' ? $this->getParent()->getClassKey() : 'start'
);
$this->d3GetSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, $oUser->getId());
$this->d3GetSession()->setVariable(
d3totp::TOTP_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
);
$oUser->logout();
$this->d3TotpGetSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, $oUser->getId());
$this->d3TotpGetSession()->setVariable(
d3totp_conf::SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
);
$sUrl = Registry::getConfig()->getShopHomeUrl() . 'cl=d3totplogin';
$this->d3GetUtils()->redirect($sUrl, false);
$this->d3TotpGetUtils()->redirect($sUrl, false);
}
}
return parent::_afterLogin($oUser);
}
/**
@ -75,23 +82,24 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function checkTotplogin()
public function d3TotpCheckTotpLogin()
{
$sTotp = Registry::getRequest()->getRequestEscapedParameter('d3totp', true);
$sTotp = implode('', Registry::getRequest()->getRequestEscapedParameter('d3totp') ?: []);
$sUserId = Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER);
/** @var d3_totp_user $oUser */
$oUser = oxNew(User::class);
$sUserId = Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER);
$oUser->load($sUserId);
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($sUserId);
try {
if (!$this->isNoTotpOrNoLogin($totp) && $this->hasValidTotp($sTotp, $totp)) {
if (!$this->d3TotpIsNoTotpOrNoLogin($totp) && $this->d3TotpHasValidTotp($sTotp, $totp)) {
// relogin, don't extract from this try block
$this->d3GetSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, $sTotp);
$this->d3GetSession()->setVariable('usr', $oUser->getId());
$this->setUser(null);
$this->d3TotpGetSession()->setVariable(d3totp_conf::SESSION_AUTH, $oUser->getId());
$this->d3TotpGetSession()->setVariable(d3totp_conf::OXID_FRONTEND_AUTH, $oUser->getId());
$this->setUser($oUser);
$this->setLoginStatus(USER_LOGIN_SUCCESS);
$this->_afterLogin($oUser);
@ -100,7 +108,7 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
return false;
}
} catch (d3totp_wrongOtpException $oEx) {
$this->d3GetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3totplogin');
$this->d3TotpGetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3totplogin');
}
return 'd3totplogin';
@ -109,7 +117,7 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
/**
* @return UtilsView
*/
public function d3GetUtilsView()
public function d3TotpGetUtilsView()
{
return Registry::getUtilsView();
}
@ -117,12 +125,12 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
/**
* @return Utils
*/
public function d3GetUtils()
public function d3TotpGetUtils()
{
return Registry::getUtils();
}
public function cancelTotpLogin()
public function d3TotpCancelTotpLogin()
{
$this->d3TotpClearSessionVariables();
@ -133,9 +141,9 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @param d3totp $totp
* @return bool
*/
public function isNoTotpOrNoLogin($totp)
public function d3TotpIsNoTotpOrNoLogin($totp)
{
return false == Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER)
return false == Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER)
|| false == $totp->isActive();
}
@ -146,25 +154,23 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @throws DatabaseConnectionException
* @throws d3totp_wrongOtpException
*/
public function hasValidTotp($sTotp, $totp)
public function d3TotpHasValidTotp($sTotp, $totp)
{
return Registry::getSession()->getVariable(d3totp::TOTP_SESSION_VARNAME) ||
(
$sTotp && $totp->verify($sTotp)
);
return Registry::getSession()->getVariable(d3totp_conf::SESSION_AUTH) ||
$totp->verify($sTotp);
}
public function d3TotpClearSessionVariables()
{
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTCLASS);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTUSER);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_NAVFORMPARAMS);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_CURRENTCLASS);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_CURRENTUSER);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_NAVFORMPARAMS);
}
/**
* @return Session
*/
public function d3GetSession()
public function d3TotpGetSession()
{
return Registry::getSession();
}

View File

@ -15,163 +15,115 @@ declare(strict_types=1);
namespace D3\Totp\Modules\Application\Controller\Admin;
use D3\TestingTools\Production\IsMockable;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use Doctrine\DBAL\DBALException;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Language;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsView;
use OxidEsales\Eshop\Core\UtilsServer;
class d3_totp_LoginController extends d3_totp_LoginController_parent
{
/**
* @return string
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function render()
{
$auth = $this->d3GetSession()->getVariable("auth");
$return = parent::render();
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($auth);
if ($auth
&& $totp->isActive()
&& !$this->d3GetSession()->getVariable(d3totp::TOTP_SESSION_VARNAME)
) {
// set auth as secured parameter;
$this->d3GetSession()->setVariable("auth", $auth);
$this->addTplParam('request_totp', true);
}
return $return;
}
use IsMockable;
/**
* @return d3totp
*/
public function d3GetTotpObject()
public function d3GetTotpObject(): d3totp
{
return oxNew(d3totp::class);
}
/**
* @return d3backupcodelist
*/
public function d3GetBackupCodeListObject()
{
return oxNew(d3backupcodelist::class);
}
/**
* @return UtilsView
*/
public function d3GetUtilsView()
{
return Registry::getUtilsView();
}
/**
* @return Session
*/
public function d3GetSession()
public function d3TotpGetSession(): Session
{
return Registry::getSession();
}
/**
* @return mixed|string
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function checklogin()
{
$sTotp = Registry::getRequest()->getRequestEscapedParameter('d3totp', true);
$totp = $this->d3GetTotpObject();
$totp->loadByUserId(Registry::getSession()->getVariable("auth"));
$return = 'login';
try {
if ($this->isNoTotpOrNoLogin($totp) && $this->hasLoginCredentials()) {
$return = parent::checklogin();
} elseif ($this->hasValidTotp($sTotp, $totp)) {
$this->d3GetSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, $sTotp);
$return = "admin_start";
}
} catch (d3totp_wrongOtpException $oEx) {
$this->d3GetUtilsView()->addErrorToDisplay($oEx);
}
return $return;
}
/**
* @return string|void
* @throws DatabaseConnectionException
*/
public function getBackupCodeCountMessage()
{
$oBackupCodeList = $this->d3GetBackupCodeListObject();
$iCount = $oBackupCodeList->getAvailableCodeCount(Registry::getSession()->getVariable("auth"));
if ($iCount < 4) {
return sprintf(
Registry::getLang()->translateString('D3_TOTP_AVAILBACKUPCODECOUNT'),
$iCount
);
}
}
/**
* @param d3totp $totp
* @return bool
*/
public function isNoTotpOrNoLogin($totp)
{
return false == $this->d3GetSession()->getVariable("auth")
|| false == $totp->isActive();
}
protected function hasLoginCredentials()
{
return Registry::getRequest()->getRequestEscapedParameter('user') &&
Registry::getRequest()->getRequestEscapedParameter('pwd');
}
/**
* @param string $sTotp
* @param d3totp $totp
* @return bool
* @throws DatabaseConnectionException
* @throws d3totp_wrongOtpException
*/
public function hasValidTotp($sTotp, $totp)
{
return Registry::getSession()->getVariable(d3totp::TOTP_SESSION_VARNAME) ||
(
$sTotp && $totp->verify($sTotp)
$this->d3TotpGetSession()->setVariable(
d3totp_conf::SESSION_ADMIN_PROFILE,
Registry::getRequest()->getRequestEscapedParameter('profile')
);
$this->d3TotpGetSession()->setVariable(
d3totp_conf::SESSION_ADMIN_CHLANGUAGE,
Registry::getRequest()->getRequestEscapedParameter('chlanguage')
);
return $this->d3CallMockableFunction([d3_totp_LoginController_parent::class, 'checklogin']);
}
public function d3CancelLogin()
public function d3totpAfterLogin()
{
$oUser = $this->d3GetUserObject();
$oUser->logout();
$myUtilsServer = $this->d3TotpGetUtilsServer();
$sProfile = $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_PROFILE);
// #533
if (isset($sProfile)) {
$aProfiles = $this->d3TotpGetSession()->getVariable("aAdminProfiles");
if ($aProfiles && isset($aProfiles[$sProfile])) {
// setting cookie to store last locally used profile
$myUtilsServer->setOxCookie("oxidadminprofile", $sProfile . "@" . implode("@", $aProfiles[$sProfile]), time() + 31536000, "/");
$this->d3TotpGetSession()->setVariable("profile", $aProfiles[$sProfile]);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_ADMIN_PROFILE);
}
} else {
//deleting cookie info, as setting profile to default
$myUtilsServer->setOxCookie("oxidadminprofile", "", time() - 3600, "/");
}
$this->d3totpAfterLoginSetLanguage();
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_ADMIN_CHLANGUAGE);
}
public function d3totpAfterLoginSetLanguage()
{
$myUtilsServer = $this->d3TotpGetUtilsServer();
$iLang = $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_CHLANGUAGE);
$aLanguages = $this->d3TotpGetLangObject()->getAdminTplLanguageArray();
if (!isset($aLanguages[$iLang])) {
$iLang = key($aLanguages);
}
$myUtilsServer->setOxCookie("oxidadminlanguage", $aLanguages[$iLang]->abbr, time() + 31536000, "/");
$this->d3TotpGetLangObject()->setTplLanguage($iLang);
}
/**
* @return User
* @param d3totp $totp
* @return bool
*/
public function d3GetUserObject()
public function d3TotpLoginMissing($totp)
{
return oxNew(User::class);
return $totp->isActive()
&& false == $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);
}
/**
* @return UtilsServer
*/
protected function d3TotpGetUtilsServer(): UtilsServer
{
return Registry::getUtilsServer();
}
/**
* @return Language
*/
protected function d3TotpGetLangObject(): Language
{
return Registry::getLang();
}
}

View File

@ -18,4 +18,6 @@ namespace D3\Totp\Modules\Application\Controller;
class d3_totp_OrderController extends d3_totp_OrderController_parent
{
use d3_totp_getUserTrait;
private $parentClass = d3_totp_OrderController_parent::class;
}

View File

@ -18,4 +18,6 @@ namespace D3\Totp\Modules\Application\Controller;
class d3_totp_PaymentController extends d3_totp_PaymentController_parent
{
use d3_totp_getUserTrait;
private $parentClass = d3_totp_PaymentController_parent::class;
}

View File

@ -18,4 +18,6 @@ namespace D3\Totp\Modules\Application\Controller;
class d3_totp_UserController extends d3_totp_UserController_parent
{
use d3_totp_getUserTrait;
private $parentClass = d3_totp_UserController_parent::class;
}

View File

@ -15,7 +15,9 @@ declare(strict_types=1);
namespace D3\Totp\Modules\Application\Controller;
use D3\TestingTools\Production\IsMockable;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
@ -23,26 +25,31 @@ use OxidEsales\Eshop\Core\Session;
trait d3_totp_getUserTrait
{
use IsMockable;
/**
* @return bool|object|User
* @throws DatabaseConnectionException
*/
public function getUser()
{
$oUser = parent::getUser();
/** @var User|null $user */
$user = $this->d3CallMockableFunction([$this->parentClass, 'getUser']);
if ($oUser instanceof User && $oUser->getId()) {
if ($user && $user->isLoaded() && $user->getId()) {
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($oUser->getId());
$totp->loadByUserId($user->getId());
if ($totp->isActive()
&& !$this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME)
&& !$this->d3TotpGetSessionObject()->getVariable(
isAdmin() ? d3totp_conf::SESSION_ADMIN_AUTH : d3totp_conf::SESSION_AUTH
)
) {
return false;
}
}
return $oUser;
return $user;
}
/**
@ -56,7 +63,7 @@ trait d3_totp_getUserTrait
/**
* @return Session
*/
public function d3GetSessionObject()
public function d3TotpGetSessionObject()
{
return Registry::getSession();
}

View File

@ -16,6 +16,7 @@ declare(strict_types=1);
namespace D3\Totp\Modules\Application\Model;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
@ -25,7 +26,10 @@ class d3_totp_user extends d3_totp_user_parent
{
$return = parent::logout();
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_VARNAME);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_AUTH);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_CURRENTUSER);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_ADMIN_AUTH);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_ADMIN_CURRENTUSER);
return $return;
}
@ -41,8 +45,23 @@ class d3_totp_user extends d3_totp_user_parent
/**
* @return Session
*/
public function d3GetSession()
public function d3TotpGetSession()
{
return Registry::getSession();
}
/**
* @return string|null
*/
public function d3TotpGetCurrentUser(): ?string
{
return $this->isAdmin() ?
($this->d3TotpGetSession()->hasVariable(d3totp_conf::SESSION_ADMIN_CURRENTUSER) ?
$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_CURRENTUSER) :
$this->d3TotpGetSession()->getVariable(d3totp_conf::OXID_ADMIN_AUTH))
:
($this->d3TotpGetSession()->hasVariable(d3totp_conf::SESSION_CURRENTUSER) ?
$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER) :
$this->d3TotpGetSession()->getVariable(d3totp_conf::OXID_FRONTEND_AUTH));
}
}

View File

@ -16,6 +16,7 @@ declare(strict_types=1);
namespace D3\Totp\Modules\Core;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
@ -32,10 +33,9 @@ class d3_totp_utils extends d3_totp_utils_parent
public function checkAccessRights()
{
$blAuth = parent::checkAccessRights();
$blAuth = $this->d3AuthHook($blAuth);
$userID = $this->d3GetSessionObject()->getVariable("auth");
$totpAuth = (bool) $this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME);
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$totpAuth = (bool) $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);
/** @var d3totp $totp */
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($userID);
@ -47,21 +47,11 @@ class d3_totp_utils extends d3_totp_utils_parent
&& $totp->isActive() === false
) {
$this->redirect('index.php?cl=d3force_2fa');
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');
if (false == defined('OXID_PHP_UNIT')) {
// @codeCoverageIgnoreStart
exit;
// @codeCoverageIgnoreEnd
}
$this->redirect('index.php?cl=d3totpadminlogin', false);
}
return $blAuth;
@ -70,7 +60,7 @@ class d3_totp_utils extends d3_totp_utils_parent
/**
* @return Session
*/
public function d3GetSessionObject()
public function d3TotpGetSessionObject()
{
return Registry::getSession();
}

View File

@ -0,0 +1,101 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Modules\Core;
use D3\TestingTools\Production\IsMockable;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
class totpSystemEventHandler extends totpSystemEventHandler_parent
{
use IsMockable;
public function onAdminLogin()
{
$this->d3RequestTotp();
$this->d3CallMockableFunction([totpSystemEventHandler_parent::class, 'onAdminLogin']);
}
protected function d3requestTotp()
{
$totp = $this->d3GetTotpObject();
$userId = $this->d3TotpGetSession()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$totp->loadByUserId($userId);
if ($this->d3TotpLoginMissing($totp)) {
/** @var d3_totp_user $user */
$user = $this->d3TotpGetUserObject();
$user->logout();
$this->d3TotpGetSession()->setVariable(d3totp_conf::SESSION_ADMIN_CURRENTUSER, $userId);
$this->getUtilsObject()->redirect(
'index.php?cl=d3totpadminlogin&amp;'.
'profile='.$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_PROFILE).'&amp;'.
'chlanguage='.$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_CHLANGUAGE),
false
);
}
}
/**
* @return d3totp
*/
public function d3GetTotpObject()
{
return oxNew(d3totp::class);
}
/**
* @return Utils
*/
public function getUtilsObject(): Utils
{
return Registry::getUtils();
}
/**
* @return Session
*/
public function d3TotpGetSession()
{
return Registry::getSession();
}
/**
* @param d3totp $totp
* @return bool
*/
public function d3TotpLoginMissing($totp)
{
return $totp->isActive()
&& false == $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);
}
/**
* @return User
*/
protected function d3TotpGetUserObject(): User
{
return oxNew(User::class);
}
}

32
src/metadata.php Normal file → Executable file
View File

@ -13,6 +13,7 @@
declare(strict_types=1);
use D3\Totp\Application\Controller\Admin\d3totpadminlogin;
use D3\Totp\Application\Controller\Admin\d3user_totp;
use D3\Totp\Application\Controller\Admin\d3force_2fa;
use D3\Totp\Application\Controller\d3_account_totp;
@ -24,12 +25,14 @@ use D3\Totp\Modules\Application\Controller\d3_totp_PaymentController;
use D3\Totp\Modules\Application\Controller\d3_totp_UserController;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use D3\Totp\Modules\Core\d3_totp_utils;
use D3\Totp\Modules\Core\totpSystemEventHandler;
use D3\Totp\Setup as ModuleSetup;
use OxidEsales\Eshop\Application\Component\UserComponent;
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Controller\OrderController;
use OxidEsales\Eshop\Application\Controller\PaymentController;
use OxidEsales\Eshop\Application\Controller\UserController;
use OxidEsales\Eshop\Core\SystemEventHandler;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Application\Model as OxidModel;
@ -47,14 +50,14 @@ $logo = '<img src="https://logos.oxidmodule.com/d3logo.svg" alt="(D3)" style="he
$aModule = [
'id' => $sModuleId,
'title' => [
'de' => $logo . ' Zwei-Faktor-Authentisierung',
'en' => $logo . ' two-factor authentication',
'de' => $logo . ' zweiter Faktor - Einmalpasswort',
'en' => $logo . ' second factor - one-time password',
],
'description' => [
'de' => 'Zwei-Faktor-Authentisierung (TOTP) f&uuml;r OXID eSales Shop',
'en' => 'Two-factor authentication (TOTP) for OXID eSales shop',
'de' => 'Einmalpasswort (TOTP) als zweiter Faktor bei der Anmeldung im OXID eSales Shop',
'en' => 'One-time password (TOTP) as second factor for login in OXID eSales shop',
],
'version' => '2.0.0.1',
'version' => '2.1.0.0',
'author' => 'D&sup3; Data Development (Inh.: Thomas Dartsch)',
'email' => 'support@shopmodule.com',
'url' => 'https://www.oxidmodule.com/',
@ -66,17 +69,20 @@ $aModule = [
LoginController::class => d3_totp_LoginController::class,
Utils::class => d3_totp_utils::class,
UserComponent::class => d3_totp_UserComponent::class,
SystemEventHandler::class => totpSystemEventHandler::class,
],
'controllers' => [
'd3user_totp' => d3user_totp::class,
'd3force_2fa' => d3force_2fa::class,
'd3totplogin' => d3totplogin::class,
'd3_account_totp' => d3_account_totp::class,
'd3totpadminlogin' => d3totpadminlogin::class,
],
'templates' => [
'd3user_totp.tpl' => 'd3/totp/Application/views/admin/tpl/d3user_totp.tpl',
'd3totplogin.tpl' => 'd3/totp/Application/views/tpl/d3totplogin.tpl',
'd3_account_totp.tpl' => 'd3/totp/Application/views/tpl/d3_account_totp.tpl',
'd3totpadminlogin.tpl' => 'd3/totp/Application/views/admin/tpl/d3totplogin.tpl',
],
'settings' => [
[
@ -101,5 +107,21 @@ $aModule = [
'block' => 'account_menu',
'file' => 'Application/views/blocks/page/account/inc/account_menu.tpl',
],
[
'template' => 'page/account/dashboard.tpl',
'block' => 'account_dashboard_col2',
'file' => 'Application/views/blocks/page/account/account_dashboard_col2_wave.tpl',
],
[
'theme' => 'flow',
'template' => 'page/account/dashboard.tpl',
'block' => 'account_dashboard_col2',
'file' => 'Application/views/blocks/page/account/account_dashboard_col2_flow.tpl',
],
[
'template' => 'widget/header/servicebox.tpl',
'block' => 'widget_header_servicebox_items',
'file' => 'Application/views/blocks/widget/header/widget_header_servicebox_items.tpl',
],
],
];

View File

@ -9,4 +9,33 @@
order: 2;
background: silver;
color: black;
}
#login label {
position: absolute;
left: -2000px;
}
#login label.show {
position: relative;
display: block;
left: 0;
}
#login .container {
display: flex;
flex-direction: row;
margin-bottom: 10px;
justify-content: center;
}
#login .digit {
height: 3em;
width: 2em;
margin: 0 2px;
text-align: center;
font-size: 1.5em;
}
#login .digit:nth-of-type(4) {
margin-left: 7px;
}

View File

@ -14,4 +14,35 @@
.cl-d3totplogin .mainforms {
margin-top: 20px;
margin-bottom: 20px;
}
#login label {
position: absolute;
left: -2000px;
}
#login label.show {
position: relative;
display: block;
left: 0;
}
#login .container {
display: flex;
flex-direction: row;
margin: 20px 0;
justify-content: center;
}
#login .digit {
width: 2em;
margin: 0 2px;
text-align: center;
height: 3em;
font-size: 1.5em;
}
#login .digit:nth-of-type(4) {
margin-left: 15px;
}

View File

@ -1,37 +0,0 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
// Include totp test config
namespace D3\Totp\tests;
use D3\ModCfg\Tests\additional_abstract;
use OxidEsales\Eshop\Core\Exception\StandardException;
include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'd3totp_config.php');
class additional extends additional_abstract
{
/**
* additional constructor.
* @throws StandardException
*/
public function __construct()
{
if (D3TOTP_REQUIRE_MODCFG) {
$this->reactivateModCfg();
}
}
}
oxNew(additional::class);

View File

@ -1,14 +0,0 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
const D3TOTP_REQUIRE_MODCFG = true;

View File

@ -3,6 +3,7 @@
namespace D3\Totp\tests\unit\Application\Controller\Admin;
use D3\Totp\Application\Controller\Admin\d3force_2fa;
use D3\Totp\Application\Model\d3totp_conf;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use PHPUnit\Framework\MockObject\MockObject;
@ -27,7 +28,7 @@ class d3force_2faTest extends d3user_totpTest
{
$expected = 'fixture';
Registry::getSession()->setVariable('auth', $expected);
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, $expected);
$this->callMethod(
$this->_oController,
@ -64,7 +65,7 @@ class d3force_2faTest extends d3user_totpTest
Registry::getConfig()->setConfigParam('D3_TOTP_ADMIN_FORCE_2FA', $force2FA);
Registry::getSession()->setVariable('auth', $givenUserId);
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, $givenUserId);
$this->assertSame(
$expected,
@ -92,7 +93,7 @@ class d3force_2faTest extends d3user_totpTest
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3GetSessionObject
* @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3TotpGetSessionObject
*/
public function testD3GetSessionObject()
{
@ -100,7 +101,7 @@ class d3force_2faTest extends d3user_totpTest
Session::class,
$this->callMethod(
$this->_oController,
'd3GetSessionObject'
'd3TotpGetSessionObject'
)
);
}

View File

@ -0,0 +1,654 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Controller\Admin;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Controller\Admin\d3totpadminlogin;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\EshopCommunity\Internal\Framework\Logger\Wrapper\LoggerWrapper;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use ReflectionException;
class d3totpadminloginTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3totpadminlogin */
protected $_oController;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oController = oxNew(d3totpadminlogin::class);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oController);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::_authorize
*/
public function testAuthorize()
{
$this->assertTrue(
$this->callMethod(
$this->_oController,
'_authorize'
)
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpGetTotpObject
*/
public function d3TotpGetTotpObjectReturnsRightInstance()
{
$this->assertInstanceOf(
d3totp::class,
$this->callMethod(
$this->_oController,
'd3TotpGetTotpObject'
)
);
}
/**
* @test
* @param $hasAuthAlready
* @param $totpActive
* @param $expected
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::isTotpIsNotRequired
* @dataProvider isTotpIsNotRequiredPassedDataProvider
*/
public function isTotpIsNotRequiredPassed($hasAuthAlready, $totpActive, $expected)
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'isActive',
'loadByUserId',
])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('isActive')->willReturn($totpActive);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'hasVariable',
])
->getMock();
$hasVariableMap = [
[d3totp_conf::SESSION_ADMIN_AUTH, $hasAuthAlready],
];
$oSessionMock->method('hasVariable')->willReturnMap($hasVariableMap);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods([
'd3TotpGetSession',
'd3TotpGetTotpObject',
])
->getMock();
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetTotpObject')->willReturn($oTotpMock);
$this->_oController = $oControllerMock;
$this->assertSame(
$expected,
$this->callMethod(
$this->_oController,
'isTotpIsNotRequired'
)
);
}
/**
* @return array
*/
public function isTotpIsNotRequiredPassedDataProvider(): array
{
return [
'auth already finished' => [true, true, true],
'auth required' => [false, true, false],
'totp inactive' => [false, false, true],
];
}
/**
* @test
* @param $userId
* @param $expected
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::isTotpLoginNotPossible
* @dataProvider isTotpLoginNotPossiblePassedDataProvider
*/
public function isTotpLoginNotPossiblePassed($userId, $expected)
{
/** @var d3_totp_user|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['d3TotpGetCurrentUser'])
->getMock();
$oUserMock->method('d3TotpGetCurrentUser')->willReturn($userId);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods(['d3TotpGetUserObject'])
->getMock();
$oControllerMock->method('d3TotpGetUserObject')->willReturn($oUserMock);
$this->_oController = $oControllerMock;
$this->assertSame(
$expected,
$this->callMethod(
$this->_oController,
'isTotpLoginNotPossible'
)
);
}
/**
* @return array
*/
public function isTotpLoginNotPossiblePassedDataProvider(): array
{
return [
'no user' => [null, true],
'has user' => ['userId', false],
];
}
/**
* @test
* @param $totpNotRequired
* @param $totpNotPossible
* @param $redirect
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::render
* @dataProvider canRenderDataProvider
*/
public function canRender($totpNotRequired, $totpNotPossible, $redirect)
{
/** @var Utils|MockObject $oUtilsMock */
$oUtilsMock = $this->getMockBuilder(Utils::class)
->onlyMethods(['redirect'])
->getMock();
$oUtilsMock
->expects(is_null($redirect) ? $this->never() : $this->once())
->method('redirect')
->with($this->identicalTo('index.php?cl='.$redirect))
->willReturn(true);
/** @var d3_totp_LoginController|MockObject $loginControllerMock */
$loginControllerMock = $this->getMockBuilder(LoginController::class)
->onlyMethods(['d3totpAfterLoginSetLanguage'])
->getMock();
$loginControllerMock->expects($this->once())->method('d3totpAfterLoginSetLanguage')
->willReturn(true);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods([
'isTotpIsNotRequired',
'isTotpLoginNotPossible',
'd3TotpGetUtils',
'd3GetLoginController',
])
->getMock();
$oControllerMock->method('isTotpIsNotRequired')->willReturn($totpNotRequired);
$oControllerMock->method('isTotpLoginNotPossible')->willReturn($totpNotPossible);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('d3GetLoginController')->willReturn($loginControllerMock);
$this->_oController = $oControllerMock;
$this->callMethod(
$this->_oController,
'render'
);
}
/**
* @return array[]
*/
public function canRenderDataProvider(): array
{
return [
'not required' => [true, false, 'admin_start'],
'not possible' => [false, true, 'login'],
'do auth' => [false, false, null],
];
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3GetBackupCodeListObject
*/
public function d3GetBackupCodeListObjectReturnsRightObject()
{
$this->assertInstanceOf(
d3backupcodelist::class,
$this->callMethod($this->_oController, 'd3GetBackupCodeListObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::getBackupCodeCountMessage
*/
public function getBackupCodeCountMessageShowMessage()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();
$oBackupCodeListMock->method('getAvailableCodeCount')->willReturn(2);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods(['d3GetBackupCodeListObject'])
->getMock();
$oControllerMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock);
$this->_oController = $oControllerMock;
$this->assertGreaterThan(
0,
strpos(
$this->callMethod($this->_oController, 'getBackupCodeCountMessage'),
' 2 '
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::getBackupCodeCountMessage
*/
public function getBackupCodeCountMessageDontShowMessage()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();
$oBackupCodeListMock->method('getAvailableCodeCount')->willReturn(10);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods(['d3GetBackupCodeListObject'])
->getMock();
$oControllerMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock);
$this->_oController = $oControllerMock;
$this->assertEmpty(
$this->callMethod($this->_oController, 'getBackupCodeCountMessage')
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3CancelLogin
*/
public function canCancelLogin()
{
/** @var d3_totp_user|MockObject $userMock */
$userMock = $this->getMockBuilder(User::class)
->onlyMethods(['logout'])
->getMock();
$userMock->expects($this->once())->method('logout')->willReturn(true);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods(['d3TotpGetUserObject'])
->getMock();
$oControllerMock->method('d3TotpGetUserObject')->willReturn($userMock);
$this->_oController = $oControllerMock;
$this->assertSame(
'login',
$this->callMethod(
$this->_oController,
'd3CancelLogin'
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpGetUserObject
*/
public function d3GetUserObjectReturnsRightObject()
{
$this->assertInstanceOf(
User::class,
$this->callMethod($this->_oController, 'd3TotpGetUserObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::checklogin
*/
public function checkloginUnvalidTotp()
{
/** @var LoggerWrapper|MockObject $loggerMock */
$loggerMock = $this->getMockBuilder(LoggerWrapper::class)
->disableOriginalConstructor()
->onlyMethods(['error', 'debug'])
->getMock();
$loggerMock->expects($this->atLeastOnce())->method('error')->willReturn(true);
$loggerMock->expects($this->atLeastOnce())->method('debug')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['loadByUserId'])
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'initNewSession',
'setVariable',
'deleteVariable',
])
->getMock();
$oSessionMock->expects($this->never())->method('initNewSession')->willReturn(false);
$oSessionMock->expects($this->never())->method('setVariable')->willReturn(false);
$oSessionMock->expects($this->never())->method('deleteVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $loginControllerMock */
$loginControllerMock = $this->getMockBuilder(LoginController::class)
->onlyMethods(['d3totpAfterLogin'])
->getMock();
$loginControllerMock->expects($this->never())->method('d3totpAfterLogin')->willReturn(true);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods([
'getLogger',
'd3TotpHasValidTotp',
'd3TotpGetSession',
'd3GetLoginController',
])
->getMock();
$oControllerMock->method('d3TotpHasValidTotp')
->willThrowException(oxNew(d3totp_wrongOtpException::class));
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('getLogger')->willReturn($loggerMock);
$oControllerMock->method('d3GetLoginController')->willReturn($loginControllerMock);
$this->_oController = $oControllerMock;
$this->callMethod(
$this->_oController,
'checklogin'
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::checklogin
*/
public function checkloginValidTotp()
{
/** @var LoggerWrapper|MockObject $loggerMock */
$loggerMock = $this->getMockBuilder(LoggerWrapper::class)
->disableOriginalConstructor()
->onlyMethods(['error', 'debug'])
->getMock();
$loggerMock->expects($this->never())->method('error')->willReturn(true);
$loggerMock->expects($this->never())->method('debug')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['loadByUserId'])
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'initNewSession',
'setVariable',
'deleteVariable',
])
->getMock();
$oSessionMock->expects($this->atLeastOnce())->method('initNewSession')->willReturn(false);
$oSessionMock->expects($this->atLeastOnce())->method('setVariable')->willReturn(false);
$oSessionMock->expects($this->atLeastOnce())->method('deleteVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $loginControllerMock */
$loginControllerMock = $this->getMockBuilder(LoginController::class)
->onlyMethods(['d3totpAfterLogin'])
->getMock();
$loginControllerMock->expects($this->once())->method('d3totpAfterLogin')->willReturn(true);
/** @var d3totpadminlogin|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3totpadminlogin::class)
->onlyMethods([
'getLogger',
'd3TotpHasValidTotp',
'd3TotpGetSession',
'd3GetLoginController',
])
->getMock();
$oControllerMock->method('d3TotpHasValidTotp')->willReturn(true);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('getLogger')->willReturn($loggerMock);
$oControllerMock->method('d3GetLoginController')->willReturn($loginControllerMock);
$this->_oController = $oControllerMock;
$this->callMethod(
$this->_oController,
'checklogin'
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpHasValidTotp
*/
public function hasValidTotpTrueSessionVarname()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_ADMIN_AUTH, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(false);
$this->assertTrue(
$this->callMethod($this->_oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpHasValidTotp
*/
public function hasValidTotpTrueValidTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_ADMIN_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(true);
$this->assertTrue(
$this->callMethod($this->_oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpHasValidTotp
*/
public function hasValidTotpFalseMissingTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_ADMIN_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willThrowException(oxNew(d3totp_wrongOtpException::class));
$this->expectException(d3totp_wrongOtpException::class);
$this->callMethod($this->_oController, 'd3TotpHasValidTotp', [null, $oTotpMock]);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpHasValidTotp
*/
public function hasValidTotpFalseUnverifiedTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_ADMIN_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(false);
$this->assertFalse(
$this->callMethod($this->_oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpGetUtils
*/
public function d3TotpGetUtilsReturnsRightInstance()
{
$this->assertInstanceOf(
Utils::class,
$this->callMethod(
$this->_oController,
'd3TotpGetUtils'
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3TotpGetSession
*/
public function d3GetSessionReturnsRightObject()
{
$this->assertInstanceOf(
Session::class,
$this->callMethod(
$this->_oController,
'd3TotpGetSession'
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::getLogger
*/
public function getLoggerReturnsRightObject()
{
$this->assertInstanceOf(
LoggerInterface::class,
$this->callMethod(
$this->_oController,
'getLogger'
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3totpadminlogin::d3GetLoginController
*/
public function d3GetLoginControllerReturnsRightObject()
{
$this->assertInstanceOf(
LoginController::class,
$this->callMethod(
$this->_oController,
'd3GetLoginController'
)
);
}
}

View File

@ -11,8 +11,11 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Controller\Admin;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Controller\Admin\d3user_totp;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
@ -24,6 +27,8 @@ use ReflectionException;
class d3user_totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3user_totp */
protected $_oController;
@ -79,7 +84,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canRenderSelectedUser()
{
/** @var User|MockObject $oControllerMock */
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods([
'getId',
@ -118,7 +123,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canRenderUnloadableUser()
{
/** @var User|MockObject $oControllerMock */
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods([
'getId',
@ -214,13 +219,13 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function cantSaveBecauseOfNotVerifiable()
{
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['save'])
->getMock();
$oBackupCodeListMock->expects($this->never())->method('save')->willReturn(true);
/** @var d3totp|MockObject $oControllerMock */
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -264,13 +269,13 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function cantSaveBecauseExistingRegistration()
{
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['save'])
->getMock();
$oBackupCodeListMock->expects($this->never())->method('save')->willReturn(true);
/** @var d3totp|MockObject $oControllerMock */
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods([
@ -314,7 +319,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canSave()
{
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods([
'save',
@ -324,7 +329,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
$oBackupCodeListMock->expects($this->once())->method('save')->willReturn(true);
$oBackupCodeListMock->method('generateBackupCodes')->willReturn(true);
/** @var d3totp|MockObject $oControllerMock */
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -373,7 +378,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
];
$_GET['editval'] = $aEditval;
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods([
'save',
@ -383,7 +388,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
$oBackupCodeListMock->expects($this->once())->method('save')->willReturn(true);
$oBackupCodeListMock->method('generateBackupCodes')->willReturn(true);
/** @var d3totp|MockObject $oControllerMock */
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -509,7 +514,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canGetAvailableBackupCodeCount()
{
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();

View File

@ -11,8 +11,11 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Controller;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Controller\d3_account_totp;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
@ -24,6 +27,8 @@ use ReflectionException;
class d3_account_totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_account_totp */
protected $_oController;
@ -83,7 +88,7 @@ class d3_account_totpTest extends d3TotpUnitTestCase
*/
public function renderReturnsLoginTemplateIfNotLoggedIn()
{
$oUser = false;
$oUser = null;
/** @var d3_account_totp|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_account_totp::class)
@ -143,7 +148,7 @@ class d3_account_totpTest extends d3TotpUnitTestCase
*/
public function canGetAvailableBackupCodeCount()
{
/** @var d3backupcodelist|MockObject $oControllerMock */
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();

View File

@ -11,11 +11,14 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Controller;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Controller\d3totplogin;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Utils;
@ -24,6 +27,8 @@ use ReflectionException;
class d3totploginTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3totplogin */
protected $_oController;
@ -36,8 +41,8 @@ class d3totploginTest extends d3TotpUnitTestCase
$this->_oController = oxNew(d3totplogin::class);
Registry::getSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTUSER);
Registry::getSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTCLASS);
Registry::getSession()->deleteVariable(d3totp_conf::SESSION_CURRENTUSER);
Registry::getSession()->deleteVariable(d3totp_conf::SESSION_CURRENTCLASS);
}
public function tearDown(): void
@ -78,7 +83,7 @@ class d3totploginTest extends d3TotpUnitTestCase
*/
public function renderDontRedirect()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, 'foo');
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, 'foo');
/** @var Utils|MockObject $oUtilsMock */
$oUtilsMock = $this->getMockBuilder(Utils::class)
@ -193,7 +198,7 @@ class d3totploginTest extends d3TotpUnitTestCase
public function canGetPreviousClass()
{
$className = "testClass";
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, $className);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTCLASS, $className);
$this->assertSame(
$className,
@ -209,7 +214,7 @@ class d3totploginTest extends d3TotpUnitTestCase
*/
public function classIsOrderStep($className, $expected)
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, $className);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTCLASS, $className);
$this->assertSame(
$expected,
@ -239,7 +244,7 @@ class d3totploginTest extends d3TotpUnitTestCase
*/
public function getIsOrderStepIsSameLikeOrderClass($className, $expected)
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, $className);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTCLASS, $className);
$this->assertSame(
$expected,

View File

@ -11,14 +11,19 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Model\Exceptions;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use ReflectionException;
class d3totp_wrongOtpExceptionTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3totp_wrongOtpException */
protected $_oModel;

View File

@ -11,14 +11,19 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Model;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3RandomGenerator;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use ReflectionException;
class d3RandomGeneratorTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3RandomGenerator */
protected $_oModel;

View File

@ -11,10 +11,13 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Model;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3backupcode;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
@ -23,6 +26,8 @@ use ReflectionException;
class d3backupcodeTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3backupcode */
protected $_oModel;
@ -109,9 +114,9 @@ class d3backupcodeTest extends d3TotpUnitTestCase
/** @var d3backupcode|MockObject $oModelMock */
$oModelMock = $this->getMockBuilder(d3backupcode::class)
->onlyMethods(['d3GetUserObject'])
->onlyMethods(['d3TotpGetUserObject'])
->getMock();
$oModelMock->method('d3GetUserObject')->willReturn($oUserMock);
$oModelMock->method('d3TotpGetUserObject')->willReturn($oUserMock);
$this->_oModel = $oModelMock;
@ -153,7 +158,7 @@ class d3backupcodeTest extends d3TotpUnitTestCase
*/
public function d3GetUserReturnCurrentUser()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, 'foobar');
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, 'foobar');
$oUser = $this->callMethod($this->_oModel, 'd3GetUser');
@ -169,13 +174,13 @@ class d3backupcodeTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Application\Model\d3backupcode::d3GetUserObject
* @covers \D3\Totp\Application\Model\d3backupcode::d3TotpGetUserObject
*/
public function d3getUserObjectReturnsRightInstance()
{
$this->assertInstanceOf(
User::class,
$this->callMethod($this->_oModel, 'd3GetUserObject')
$this->callMethod($this->_oModel, 'd3TotpGetUserObject')
);
}
}

View File

@ -11,8 +11,11 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Model;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3backupcode;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
@ -25,6 +28,8 @@ use ReflectionException;
class d3backupcodelistTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3backupcodelist */
protected $_oModel;
@ -52,7 +57,7 @@ class d3backupcodelistTest extends d3TotpUnitTestCase
*/
public function generateBackupCodes()
{
/** @var FrontendController|MockObject $oConfigMock */
/** @var FrontendController|MockObject $oViewMock */
$oViewMock = $this->getMockBuilder(FrontendController::class)
->addMethods(['setBackupCodes'])
->getMock();

View File

@ -11,9 +11,12 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Application\Model;
use BaconQrCode\Writer;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Factory\BaconQrCodeFactory;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
@ -29,6 +32,8 @@ use stdClass;
class d3totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3totp */
protected $_oModel;
@ -862,7 +867,7 @@ class d3totpTest extends d3TotpUnitTestCase
->onlyMethods(['d3Base64_decode'])
->getMock();
$oModelMock->method('d3Base64_decode')->willReturn(
str_pad('foobar', 50, 0, STR_PAD_LEFT)
str_pad('foobar', 50, '0', STR_PAD_LEFT)
);
$this->_oModel = $oModelMock;

View File

@ -11,12 +11,17 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Component;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\Modules\Application\Component\d3_totp_UserComponent;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use InvalidArgumentException;
use OxidEsales\Eshop\Application\Component\UserComponent;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Controller\BaseController;
@ -29,34 +34,14 @@ use ReflectionException;
class d3_totp_UserComponentTest extends d3TotpUnitTestCase
{
/** @var d3_totp_UserComponent */
protected $_oController;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oController = oxNew(UserComponent::class);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oController);
}
use CanAccessRestricted;
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
*/
public function loginFailsIfNoUserLoggedIn()
public function afterLoginFailsIfNoUserLoggedIn()
{
$oUser = false;
@ -82,31 +67,28 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3GetSession',
'd3GetUtils',
'd3TotpGetSession',
'd3TotpGetUtils',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUser);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$_GET['lgn_usr'] = 'username';
$this->expectException(InvalidArgumentException::class);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'login');
$this->callMethod($oControllerMock, '_afterLogin', [$oUser]);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login
* @dataProvider loginFailTotpNotActiveOrAlreadyCheckedDataProvider
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
* @dataProvider afterLoginFailTotpNotActiveOrAlreadyCheckedDataProvider
*/
public function loginFailTotpNotActiveOrAlreadyChecked($isActive, $checked)
public function afterLoginFailTotpNotActiveOrAlreadyChecked($isActive, $checked)
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
@ -145,28 +127,22 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3GetSession',
'd3GetUtils',
'd3TotpGetSession',
'd3TotpGetUtils',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUserMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$_GET['lgn_usr'] = 'username';
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'login');
$this->callMethod($oControllerMock, '_afterLogin', [$oUserMock]);
}
/**
* @return array
*/
public function loginFailTotpNotActiveOrAlreadyCheckedDataProvider(): array
public function afterLoginFailTotpNotActiveOrAlreadyCheckedDataProvider(): array
{
return [
'TOTP not active, not checked' => [false, null],
@ -178,9 +154,9 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
*/
public function loginPass()
public function afterLoginPass()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
@ -225,24 +201,18 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3GetSession',
'd3GetUtils',
'getParent'
'd3TotpGetSession',
'd3TotpGetUtils',
'getParent',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUserMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('getParent')->willReturn($oParentMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$_GET['lgn_usr'] = 'username';
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'login');
$this->callMethod($oControllerMock, '_afterLogin', [$oUserMock]);
}
/**
@ -252,16 +222,19 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
*/
public function d3GetTotpObjectReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod($this->_oController, 'd3GetTotpObject')
$this->callMethod($oController, 'd3GetTotpObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
*/
public function checkTotploginNoTotpLogin()
{
@ -281,29 +254,27 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'd3GetTotpObject',
'd3GetSession',
'd3TotpGetSession',
])
->getMock();
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->expects($this->never())->method('hasValidTotp')->willReturn(false);
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->expects($this->never())->method('d3TotpHasValidTotp')->willReturn(false);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->assertSame(
'd3totplogin',
$this->callMethod($this->_oController, 'checkTotplogin')
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
*/
public function checkTotploginUnvalidTotp()
{
@ -320,7 +291,7 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oSessionMock->expects($this->never())->method('setVariable');
/** @var d3totp_wrongOtpException|MockObject $oUtilsViewMock */
/** @var d3totp_wrongOtpException|MockObject $oTotpExceptionMock */
$oTotpExceptionMock = $this->getMockBuilder(d3totp_wrongOtpException::class)
->disableOriginalConstructor()
->getMock();
@ -334,31 +305,29 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3GetUtilsView',
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'd3TotpGetUtilsView',
'd3GetTotpObject',
'd3GetSession',
'd3TotpGetSession',
])
->getMock();
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('hasValidTotp')->willThrowException($oTotpExceptionMock);
$oControllerMock->method('d3GetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('d3TotpHasValidTotp')->willThrowException($oTotpExceptionMock);
$oControllerMock->method('d3TotpGetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->assertSame(
'd3totplogin',
$this->callMethod($this->_oController, 'checkTotplogin')
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
*/
public function checkTotploginValidTotp()
{
@ -384,47 +353,64 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3GetUtilsView',
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'd3TotpGetUtilsView',
'd3GetTotpObject',
'd3GetSession',
'setLoginStatus'
'd3TotpGetSession',
'setLoginStatus',
])
->getMock();
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('hasValidTotp')->willReturn(true);
$oControllerMock->method('d3GetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('d3TotpHasValidTotp')->willReturn(true);
$oControllerMock->method('d3TotpGetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->expects($this->once())->method('setLoginStatus')->with(
$this->identicalTo(USER_LOGIN_SUCCESS)
);
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($this->_oController, 'checkTotplogin')
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3GetUtilsView
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpGetUtilsView
*/
public function d3GetUtilsViewReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
UtilsView::class,
$this->callMethod($this->_oController, 'd3GetUtilsView')
$this->callMethod($oController, 'd3TotpGetUtilsView')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::cancelTotpLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpGetUtils
*/
public function d3GetUtilsReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
Utils::class,
$this->callMethod($oController, 'd3TotpGetUtils')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCancelTotpLogin
*/
public function canCancelTotpLogin()
{
@ -434,19 +420,17 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oControllerMock->expects($this->once())->method('d3TotpClearSessionVariables')->willReturn(false);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'cancelTotpLogin');
$this->callMethod($oControllerMock, 'd3TotpCancelTotpLogin');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginTrueNoSessionVariable()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, false);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -455,19 +439,22 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('isActive')->willReturn(true);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertTrue(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginTrueTotpNotActive()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, true);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -476,19 +463,22 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('isActive')->willReturn(false);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertTrue(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginFalse()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, true);
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -497,19 +487,22 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('isActive')->willReturn(true);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertFalse(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
*/
public function hasValidTotpTrueSessionVarname()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, true);
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -518,19 +511,22 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('verify')->willReturn(false);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertTrue(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
$this->callMethod($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
*/
public function hasValidTotpTrueValidTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -539,40 +535,45 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('verify')->willReturn(true);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertTrue(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
$this->callMethod($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
*/
public function hasValidTotpFalseMissingTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(true);
$oTotpMock->method('verify')->willThrowException(oxNew(d3totp_wrongOtpException::class));
$this->assertFalse(
$this->callMethod($this->_oController, 'hasValidTotp', [null, $oTotpMock])
);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->expectException(d3totp_wrongOtpException::class);
$this->callMethod($oController, 'd3TotpHasValidTotp', [null, $oTotpMock]);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
*/
public function hasValidTotpFalseUnverifiedTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -581,8 +582,11 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('verify')->willReturn(false);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertFalse(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
$this->callMethod($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
);
}
@ -601,25 +605,26 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods(['d3GetSession'])
->onlyMethods(['d3TotpGetSession'])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'd3TotpClearSessionVariables');
$this->callMethod($oControllerMock, 'd3TotpClearSessionVariables');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3GetSession
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpGetSession
*/
public function d3GetSessionReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oController, 'd3GetSession')
$this->callMethod($oController, 'd3TotpGetSession')
);
}
}

View File

@ -11,23 +11,28 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller\Admin;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use Exception;
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Language;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsView;
use OxidEsales\Eshop\Core\UtilsServer;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_totp_LoginController */
protected $_oController;
@ -48,186 +53,6 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
unset($this->_oController);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::render
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getViewDataElement
*/
public function canRenderNoAuth()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'isActive',
'loadByUserId',
])
->disableOriginalConstructor()
->getMock();
$oTotpMock->expects($this->never())->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'getVariable',
'setVariable',
])
->getMock();
$oSessionMock->method('getVariable')->will($this->onConsecutiveCalls(false, true));
$oSessionMock->expects($this->never())->method('setVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetSession',
'd3GetTotpObject',
])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$this->_oController = $oControllerMock;
$this->assertSame('login.tpl', $this->callMethod($this->_oController, 'render'));
$this->assertNotTrue($this->callMethod($this->_oController, 'getViewDataElement', ['request_totp']));
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::render
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getViewDataElement
*/
public function canRenderTotpNotActive()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods([
'isActive',
'loadByUserId',
])
->getMock();
$oTotpMock->expects($this->once())->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'getVariable',
'setVariable',
])
->getMock();
$oSessionMock->method('getVariable')->will($this->onConsecutiveCalls(true, true));
$oSessionMock->expects($this->never())->method('setVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetSession',
'd3GetTotpObject',
])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$this->_oController = $oControllerMock;
$this->assertSame('login.tpl', $this->callMethod($this->_oController, 'render'));
$this->assertNotTrue($this->callMethod($this->_oController, 'getViewDataElement', ['request_totp']));
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::render
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getViewDataElement
*/
public function canRenderInTotpLoginProcess()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'isActive',
'loadByUserId',
])
->disableOriginalConstructor()
->getMock();
$oTotpMock->expects($this->once())->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'getVariable',
'setVariable',
])
->getMock();
$oSessionMock->method('getVariable')->will($this->onConsecutiveCalls(true, true));
$oSessionMock->expects($this->never())->method('setVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetSession',
'd3GetTotpObject',
])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$this->_oController = $oControllerMock;
$this->assertSame('login.tpl', $this->callMethod($this->_oController, 'render'));
$this->assertNotTrue($this->callMethod($this->_oController, 'getViewDataElement', ['request_totp']));
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::render
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getViewDataElement
*/
public function canRenderRequestTotp()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'isActive',
'loadByUserId',
])
->disableOriginalConstructor()
->getMock();
$oTotpMock->expects($this->once())->method('isActive')->willReturn(true);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods([
'getVariable',
'setVariable',
])
->getMock();
$oSessionMock->method('getVariable')->will($this->onConsecutiveCalls(true, false));
$oSessionMock->expects($this->once())->method('setVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetSession',
'd3GetTotpObject',
])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$this->_oController = $oControllerMock;
$this->assertSame('login.tpl', $this->callMethod($this->_oController, 'render'));
$this->assertTrue($this->callMethod($this->_oController, 'getViewDataElement', ['request_totp']));
}
/**
* @test
* @throws ReflectionException
@ -244,39 +69,13 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3GetBackupCodeListObject
*/
public function d3GetBackupCodeListObjectReturnsRightObject()
{
$this->assertInstanceOf(
d3backupcodelist::class,
$this->callMethod($this->_oController, 'd3GetBackupCodeListObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3GetUtilsView
*/
public function d3GetUtilsViewReturnsRightObject()
{
$this->assertInstanceOf(
UtilsView::class,
$this->callMethod($this->_oController, 'd3GetUtilsView')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3GetSession
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpGetSession
*/
public function d3GetSessionReturnsRightObject()
{
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oController, 'd3GetSession')
$this->callMethod($this->_oController, 'd3TotpGetSession')
);
}
@ -284,47 +83,21 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
* @dataProvider checkloginNoTotpDataProvider
*/
public function checkloginNoTotp($hasLoginCredentials)
public function canChecklogin()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['loadByUserId'])
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
$fixture = 'returnString';
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetTotpObject',
'isNoTotpOrNoLogin',
'hasValidTotp',
'hasLoginCredentials',
])
->onlyMethods(['d3CallMockableFunction'])
->getMock();
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->method('hasValidTotp')->willReturn(false);
$oControllerMock->method('hasLoginCredentials')->willReturn($hasLoginCredentials);
$oControllerMock->method('d3CallMockableFunction')->willReturn($fixture);
$this->_oController = $oControllerMock;
if ($hasLoginCredentials) {
// workaround, because test case runs into parent call, stop execution with exception and check thrown
/** @var Session|MockObject $sessionMock */
$sessionMock = $this->getMockBuilder(Session::class)
->disableOriginalConstructor()
->onlyMethods(['initNewSession'])
->getMock();
$sessionMock->method('initNewSession')->willThrowException(new Exception('foo'));
Registry::set(Session::class, $sessionMock);
$this->expectException(Exception::class);
}
$this->assertSame(
'login',
$fixture,
$this->callMethod(
$this->_oController,
'checklogin'
@ -332,417 +105,202 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
);
}
/**
* @test
*
* @param $selectedProfile
* @param $setCookie
* @param $expectedCookie
* @param $setSession
*
* @throws ReflectionException
* @dataProvider canRunTotpAfterLoginDataProvider
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3totpAfterLogin
*/
public function canRunTotpAfterLogin($selectedProfile, $setCookie, $expectedCookie, $setSession)
{
/** @var Session|MockObject $sessionMock */
$sessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable', 'setVariable'])
->getMock();
$variableMap = [
[d3totp_conf::SESSION_ADMIN_PROFILE, $selectedProfile],
['aAdminProfiles', [
0 => ['abc', 0],
1 => ['def', 1],
2 => ['geh', 2],
]],
];
$sessionMock->method('getVariable')->willReturnMap($variableMap);
$sessionMock->expects($setSession)->method('setVariable')->willReturnMap($variableMap);
/** @var UtilsServer|MockObject $utilsServerMock */
$utilsServerMock = $this->getMockBuilder(UtilsServer::class)
->onlyMethods(['setOxCookie'])
->getMock();
$utilsServerMock->expects($setCookie)->method('setOxCookie')->with(
$this->anything(),
$expectedCookie
);
/** @var d3_totp_LoginController|MockObject $sut */
$sut = $this->getMockBuilder(LoginController::class)
->onlyMethods(['d3TotpGetUtilsServer', 'd3TotpGetSession', 'd3totpAfterLoginSetLanguage'])
->getMock();
$sut->method('d3TotpGetUtilsServer')->willReturn($utilsServerMock);
$sut->method('d3TotpGetSession')->willReturn($sessionMock);
$sut->expects($this->once())->method('d3totpAfterLoginSetLanguage')->willReturn($sessionMock);
$this->callMethod(
$sut,
'd3totpAfterLogin'
);
}
/**
* @return array
*/
public function checkloginNoTotpDataProvider(): array
public function canRunTotpAfterLoginDataProvider(): array
{
return [
'no totp, no login credentials' => [false],
'no totp, given login credentials' => [true],
'no profile selected' => [null, $this->once(), '', $this->never()],
'valid profile selected' => [2, $this->once(), '2@geh@2', $this->once()],
'invalid profile selected' => [5, $this->never(), false, $this->never()],
];
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3totpAfterLoginSetLanguage
* @dataProvider canRunTotpAfterLoginSetLanguageDataProvider
*/
public function checkloginInvalidTotp()
public function canRunTotpAfterLoginSetLanguage($languageId)
{
/** @var d3totp_wrongOtpException|MockObject $oUtilsViewMock */
$oTotpExceptionMock = $this->getMockBuilder(d3totp_wrongOtpException::class)
->disableOriginalConstructor()
/** @var Session|MockObject $sessionMock */
$sessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$sessionMock->method('getVariable')->willReturn($languageId);
/** @var UtilsServer|MockObject $utilsServerMock */
$utilsServerMock = $this->getMockBuilder(UtilsServer::class)
->onlyMethods(['setOxCookie'])
->getMock();
$utilsServerMock->expects($this->once())->method('setOxCookie');
/** @var UtilsView|MockObject $utilsViewMock */
$utilsViewMock = $this->getMockBuilder(UtilsView::class)
->onlyMethods(['addErrorToDisplay'])
/** @var Language|MockObject $langMock */
$langMock = $this->getMockBuilder(Language::class)
->onlyMethods(['setTplLanguage'])
->getMock();
$utilsViewMock->expects($this->once())->method('addErrorToDisplay')->willReturn(true);
$langMock->expects($this->once())->method('setTplLanguage');
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['loadByUserId'])
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $sut */
$sut = $this->getMockBuilder(LoginController::class)
->onlyMethods(['d3TotpGetUtilsServer', 'd3TotpGetSession', 'd3TotpGetLangObject'])
->getMock();
$sut->method('d3TotpGetUtilsServer')->willReturn($utilsServerMock);
$sut->method('d3TotpGetSession')->willReturn($sessionMock);
$sut->method('d3TotpGetLangObject')->willReturn($langMock);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetTotpObject',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3GetUtilsView',
])
->getMock();
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->method('hasValidTotp')->willThrowException($oTotpExceptionMock);
$oControllerMock->method('d3GetUtilsView')->willReturn($utilsViewMock);
$this->_oController = $oControllerMock;
$this->assertSame(
'login',
$this->callMethod($this->_oController, 'checklogin')
$this->callMethod(
$sut,
'd3totpAfterLoginSetlanguage'
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
* @return array
*/
public function checkloginValidTotp()
public function canRunTotpAfterLoginSetLanguageDataProvider(): array
{
/** @var UtilsView|MockObject $utilsViewMock */
$utilsViewMock = $this->getMockBuilder(UtilsView::class)
->onlyMethods(['addErrorToDisplay'])
->getMock();
$utilsViewMock->expects($this->never())->method('addErrorToDisplay')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['loadByUserId'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
->getMock();
$oSessionMock->expects($this->once())->method('setVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3GetTotpObject',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3GetUtilsView',
'd3GetSession',
])
->getMock();
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->method('hasValidTotp')->willReturn(true);
$oControllerMock->method('d3GetUtilsView')->willReturn($utilsViewMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertSame('admin_start', $this->callMethod($this->_oController, 'checklogin'));
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getBackupCodeCountMessage
*/
public function getBackupCodeCountMessageShowMessage()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();
$oBackupCodeListMock->method('getAvailableCodeCount')->willReturn(2);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetBackupCodeListObject'])
->getMock();
$oControllerMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock);
$this->_oController = $oControllerMock;
$this->assertGreaterThan(
0,
strpos(
$this->callMethod($this->_oController, 'getBackupCodeCountMessage'),
' 2 '
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getBackupCodeCountMessage
*/
public function getBackupCodeCountMessageDontShowMessage()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();
$oBackupCodeListMock->method('getAvailableCodeCount')->willReturn(10);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetBackupCodeListObject'])
->getMock();
$oControllerMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock);
$this->_oController = $oControllerMock;
$this->assertEmpty(
$this->callMethod($this->_oController, 'getBackupCodeCountMessage')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginIsAuth()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['isActive'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('isActive')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$oSessionMock->method('getVariable')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetSession'])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginTotpNotActive()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['isActive'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('isActive')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$oSessionMock->method('getVariable')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetSession'])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginPass()
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['isActive'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('isActive')->willReturn(false);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$oSessionMock->method('getVariable')->willReturn(false);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetSession'])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertTrue(
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasValidTotp
*/
public function hasValidTotpTrueSessionVarname()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(false);
$this->assertTrue(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasValidTotp
*/
public function hasValidTotpTrueValidTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(true);
$this->assertTrue(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasValidTotp
*/
public function hasValidTotpFalseMissingTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(true);
$this->assertFalse(
$this->callMethod($this->_oController, 'hasValidTotp', [null, $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasValidTotp
*/
public function hasValidTotpFalseUnverifiedTotp()
{
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willReturn(false);
$this->assertFalse(
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3CancelLogin
*/
public function d3CancelLoginPass()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['logout'])
->getMock();
$oUserMock->expects($this->once())->method('logout')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3GetUserObject'])
->getMock();
$oControllerMock->method('d3GetUserObject')->willReturn($oUserMock);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'd3CancelLogin');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3GetUserObject
*/
public function d3GetUserObjectReturnsRightObject()
{
$this->assertInstanceOf(
User::class,
$this->callMethod($this->_oController, 'd3GetUserObject')
);
return [
'existing language' => [0],
'not existing language' => [50],
];
}
/**
* @test
* @param $totpActive
* @param $loggedin
* @param $expected
* @return void
* @throws ReflectionException
* @dataProvider hasLoginCredentialsDataProvider
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasLoginCredentials
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpLoginMissing
* @dataProvider d3TotpLoginMissingTestDataProvider
*/
public function hasLoginCredentials($user, $pass, $expected)
public function d3TotpLoginMissingTest($totpActive, $loggedin, $expected)
{
$_GET['user'] = $user;
$_GET['pwd'] = $pass;
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['isActive'])
->getMock();
$oTotpMock->method('isActive')->willReturn($totpActive);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$oSessionMock->method('getVariable')->with(d3totp_conf::SESSION_ADMIN_AUTH)->willReturn($loggedin);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3TotpGetSession',
])
->getMock();
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertSame(
$expected,
$this->callMethod(
$this->_oController,
'hasLoginCredentials'
'd3TotpLoginMissing',
[$oTotpMock]
)
);
}
/**
* @return array[]
* @return array
*/
public function hasLoginCredentialsDataProvider(): array
public function d3TotpLoginMissingTestDataProvider(): array
{
return [
'user only' => ['user', null, false],
'pass only' => [null, 'password', false],
'both' => ['user', 'password', true],
'totp not active, not logged in'=> [false, false, false],
'totp active, logged in' => [true, true, false],
'totp active, not logged in' => [true, false, true],
'totp not active, logged in' => [false, true, false],
];
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpGetUtilsServer
*/
public function d3GetUtilsServerObjectReturnsRightObject()
{
$this->assertInstanceOf(
UtilsServer::class,
$this->callMethod($this->_oController, 'd3TotpGetUtilsServer')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpGetLangObject
*/
public function d3GetLangObjectReturnsRightObject()
{
$this->assertInstanceOf(
Language::class,
$this->callMethod($this->_oController, 'd3TotpGetLangObject')
);
}
}

View File

@ -11,9 +11,12 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller;
use D3\Totp\Modules\Application\Controller\d3_totp_OrderController;
use D3\Totp\Modules\Application\Controller\d3_totp_OrderController_parent;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Application\Controller\OrderController;
use PHPUnit\Framework\MockObject\MockObject;
@ -22,25 +25,5 @@ class d3_totp_OrderControllerTest extends d3TotpUnitTestCase
{
use d3_totp_getUserTestTrait;
/** @var d3_totp_OrderController|MockObject */
protected $_oController;
protected $sControllerClass = OrderController::class;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oController = oxNew(OrderController::class);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oController);
}
}

View File

@ -11,6 +11,8 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller;
use D3\Totp\Modules\Application\Controller\d3_totp_PaymentController;
@ -21,25 +23,5 @@ class d3_totp_PaymentControllerTest extends d3TotpUnitTestCase
{
use d3_totp_getUserTestTrait;
/** @var d3_totp_PaymentController */
protected $_oController;
protected $sControllerClass = PaymentController::class;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oController = oxNew(PaymentController::class);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oController);
}
}

View File

@ -11,6 +11,8 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller;
use D3\Totp\Modules\Application\Controller\d3_totp_UserController;
@ -21,25 +23,5 @@ class d3_totp_UserControllerTest extends d3TotpUnitTestCase
{
use d3_totp_getUserTestTrait;
/** @var d3_totp_UserController */
protected $_oController;
protected $sControllerClass = UserController::class;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oController = oxNew(UserController::class);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oController);
}
}

View File

@ -11,10 +11,15 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Modules\Application\Controller\d3_totp_OrderController;
use D3\Totp\Modules\Application\Controller\d3_totp_PaymentController;
use D3\Totp\Modules\Application\Controller\d3_totp_UserController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Session;
use PHPUnit\Framework\MockObject\MockObject;
@ -22,6 +27,27 @@ use ReflectionException;
trait d3_totp_getUserTestTrait
{
use CanAccessRestricted;
protected $userFixtureId = 'userIdFixture1';
/** @var User */
protected $userFixture;
public function setUp(): void
{
$this->userFixture = oxNew(User::class);
$this->userFixture->setId($this->userFixtureId);
$this->userFixture->assign(['oxlname' => __METHOD__]);
$this->userFixture->save();
$this->userFixture->load($this->userFixtureId);
}
public function tearDown(): void
{
$this->userFixture->delete($this->userFixtureId);
}
/**
* @test
* @throws ReflectionException
@ -31,16 +57,14 @@ trait d3_totp_getUserTestTrait
*/
public function getUserHasNoUser()
{
/** @var d3_totp_orderController|MockObject $oControllerMock */
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods(['d3GetTotpObject'])
->getMock();
$oControllerMock->expects($this->never())->method('d3GetTotpObject');
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($this->_oController, 'getUser')
$this->callMethod($oControllerMock, 'getUser')
);
}
@ -53,12 +77,6 @@ trait d3_totp_getUserTestTrait
*/
public function getUserTotpNotActive()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['getId'])
->getMock();
$oUserMock->method('getId')->willReturn('foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
@ -76,24 +94,23 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|MockObject $oControllerMock */
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3GetSessionObject',
'd3TotpGetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$this->assertSame(
$oUserMock,
$this->callMethod($this->_oController, 'getUser')
$this->userFixture,
$this->callMethod($oControllerMock, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -105,12 +122,6 @@ trait d3_totp_getUserTestTrait
*/
public function getUserTotpFinished()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['getId'])
->getMock();
$oUserMock->method('getId')->willReturn('foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
@ -127,24 +138,23 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(true);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|MockObject $oControllerMock */
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3GetSessionObject',
'd3TotpGetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$this->assertSame(
$oUserMock,
$this->callMethod($this->_oController, 'getUser')
$this->userFixture,
$this->callMethod($oControllerMock, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -156,12 +166,6 @@ trait d3_totp_getUserTestTrait
*/
public function getUserTotpNotFinished()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['getId'])
->getMock();
$oUserMock->method('getId')->willReturn('foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
@ -179,23 +183,22 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(true);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|MockObject $oControllerMock */
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3GetSessionObject',
'd3TotpGetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$this->assertFalse(
$this->callMethod($this->_oController, 'getUser')
$this->callMethod($oControllerMock, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -207,26 +210,32 @@ trait d3_totp_getUserTestTrait
*/
public function d3GetTotpObjectReturnsRightObject()
{
/** @var d3_totp_UserController|d3_totp_PaymentController|d3_totp_OrderController $oController */
$oController = oxNew($this->sControllerClass);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod($this->_oController, 'd3GetTotpObject')
$this->callMethod($oController, 'd3GetTotpObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_OrderController::d3GetSessionObject
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_PaymentController::d3GetSessionObject
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_UserController::d3GetSessionObject
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_OrderController::d3TotpGetSessionObject
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_PaymentController::d3TotpGetSessionObject
* @covers \D3\Totp\Modules\Application\Controller\d3_totp_UserController::d3TotpGetSessionObject
*/
public function d3GetSessionObjectReturnsRightObject()
{
/** @var d3_totp_UserController|d3_totp_PaymentController|d3_totp_OrderController $oController */
$oController = oxNew($this->sControllerClass);
$this->assertInstanceOf(
Session::class,
$this->callMethod(
$this->_oController,
'd3GetSessionObject'
$oController,
'd3TotpGetSessionObject'
)
);
}

View File

@ -11,9 +11,13 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Model;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Application\Model\User;
@ -23,25 +27,7 @@ use ReflectionException;
class d3_totp_userTest extends d3TotpUnitTestCase
{
/** @var d3_totp_user */
protected $_oModel;
/**
* setup basic requirements
*/
public function setUp(): void
{
parent::setUp();
$this->_oModel = oxNew(User::class);
}
public function tearDown(): void
{
parent::tearDown();
unset($this->_oModel);
}
use CanAccessRestricted;
/**
* @test
@ -54,19 +40,19 @@ class d3_totp_userTest extends d3TotpUnitTestCase
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['deleteVariable'])
->getMock();
$oSessionMock->expects($this->once())->method('deleteVariable')->willReturn(true);
$oSessionMock->expects($this->atLeast(2))->method('deleteVariable')->willReturn(true);
/** @var d3_totp_user|MockObject $oModelMock */
$oModelMock = $this->getMockBuilder(User::class)
->onlyMethods(['d3GetSession'])
->onlyMethods(['d3TotpGetSession'])
->getMock();
$oModelMock->method('d3GetSession')->willReturn($oSessionMock);
$oModelMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->_oModel = $oModelMock;
$sut = $oModelMock;
$this->assertTrue(
$this->callMethod(
$this->_oModel,
$sut,
'logout'
)
);
@ -79,22 +65,90 @@ class d3_totp_userTest extends d3TotpUnitTestCase
*/
public function d3getTotpReturnsRightInstance()
{
$sut = oxNew(User::class);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod($this->_oModel, 'd3getTotp')
$this->callMethod(
$sut,
'd3getTotp'
)
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Model\d3_totp_user::d3GetSession
* @covers \D3\Totp\Modules\Application\Model\d3_totp_user::d3TotpGetSession
*/
public function d3GetSessionReturnsRightInstance()
{
$sut = oxNew(User::class);
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oModel, 'd3GetSession')
$this->callMethod(
$sut,
'd3TotpGetSession'
)
);
}
/**
* @test
* @param $currentUser
* @param $isAdmin
* @param $adminAuth
* @param $frontendAuth
* @param $expected
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Model\d3_totp_user::d3TotpGetCurrentUser
* @dataProvider d3TotpGetCurrentUserTestDataProvider
*/
public function d3TotpGetCurrentUserTest($currentUser, $isAdmin, $adminAuth, $frontendAuth, $expected)
{
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['hasVariable', 'getVariable'])
->getMock();
$oSessionMock->expects($this->once())->method('hasVariable')->willReturn((bool) $currentUser);
$getVariableMap = [
[d3totp_conf::SESSION_CURRENTUSER, $currentUser],
[d3totp_conf::SESSION_ADMIN_CURRENTUSER, $currentUser],
[d3totp_conf::OXID_ADMIN_AUTH, $adminAuth],
[d3totp_conf::OXID_FRONTEND_AUTH, $frontendAuth],
];
$oSessionMock->method('getVariable')->willReturnMap($getVariableMap);
/** @var d3_totp_user|MockObject $oModelMock */
$oModelMock = $this->getMockBuilder(User::class)
->onlyMethods(['d3TotpGetSession', 'isAdmin'])
->getMock();
$oModelMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oModelMock->method('isAdmin')->willReturn($isAdmin);
$sut = $oModelMock;
$this->assertSame(
$expected,
$this->callMethod(
$sut,
'd3TotpGetCurrentUser'
)
);
}
/**
* @return array[]
*/
public function d3TotpGetCurrentUserTestDataProvider(): array
{
return [
'adm login request' => ['currentFixture', true, 'adminFixture', 'frontendFixture', 'currentFixture'],
'frnt login request' => ['currentFixture', false, 'adminFixture', 'frontendFixture', 'currentFixture'],
'admin auth' => [null, true, 'adminFixture', 'frontendFixture', 'adminFixture'],
'frontend auth' => [null, false, 'adminFixture', 'frontendFixture', 'frontendFixture'],
];
}
}

View File

@ -11,9 +11,13 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Core;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use D3\Totp\Modules\Core\d3_totp_utils;
use D3\Totp\tests\unit\d3TotpUnitTestCase;
use OxidEsales\Eshop\Core\Config;
@ -25,6 +29,8 @@ use ReflectionException;
class d3_totp_utilsTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_totp_utils */
protected $_oCoreClass;
@ -52,7 +58,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsNoAuth()
{
Registry::getSession()->setVariable("auth", false);
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -85,7 +91,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsForce2FA()
{
Registry::getSession()->setVariable("auth", false);
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -122,7 +128,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpNotActive()
{
Registry::getSession()->setVariable("auth", 'foo');
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -159,7 +165,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpFinished()
{
Registry::getSession()->setVariable("auth", 'foo');
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
@ -182,13 +188,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
$oCoreMock = $this->getMockBuilder(Utils::class)
->onlyMethods([
'd3GetTotpObject',
'd3GetSessionObject',
'd3TotpGetSessionObject',
'fetchRightsForUser',
'redirect',
])
->getMock();
$oCoreMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oCoreMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('fetchRightsForUser')->willReturn('malladmin');
$oCoreMock->expects($this->never())->method('redirect')->willReturn(true);
@ -206,7 +212,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpUnfinished()
{
Registry::getSession()->setVariable("auth", 'foo');
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
@ -235,13 +241,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
$oCoreMock = $this->getMockBuilder(Utils::class)
->onlyMethods([
'd3GetTotpObject',
'd3GetSessionObject',
'd3TotpGetSessionObject',
'fetchRightsForUser',
'redirect',
])
->getMock();
$oCoreMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oCoreMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('fetchRightsForUser')->willReturn('malladmin');
$oCoreMock->expects($this->once())->method('redirect')->willReturn(true);
@ -253,13 +259,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\d3_totp_utils::d3GetSessionObject
* @covers \D3\Totp\Modules\Core\d3_totp_utils::d3TotpGetSessionObject
*/
public function d3GetSessionObjectReturnsRightInstance()
{
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oCoreClass, 'd3GetSessionObject')
$this->callMethod($this->_oCoreClass, 'd3TotpGetSessionObject')
);
}

View File

@ -0,0 +1,253 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace unit\Modules\Core;
use D3\TestingTools\Development\CanAccessRestricted;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use D3\Totp\Modules\Core\totpSystemEventHandler;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\SystemEventHandler;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\TestingLibrary\UnitTestCase;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class totpSystemEventHandlerTest extends UnitTestCase
{
use CanAccessRestricted;
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::onAdminLogin
*/
public function runOnAdminLogin()
{
/** @var totpSystemEventHandler|MockObject $sut */
$sut = $this->getMockBuilder(SystemEventHandler::class)
->onlyMethods(['d3CallMockableFunction', 'd3requestTotp'])
->getMock();
$sut->method('d3CallMockableFunction')->willReturn(true);
$sut->expects($this->once())->method('d3requestTotp')->willReturn(true);
$this->callMethod(
$sut,
'onAdminLogin'
);
}
/**
* @test
*
* @param $totpMissing
* @param $doLogout
* @param $doRedirect
*
* @return void
* @throws ReflectionException
* @dataProvider canRequestTotpDataProvider
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::d3requestTotp
*/
public function canRequestTotp($totpMissing, $doLogout, $doRedirect)
{
/** @var Session|MockObject $sessionMock */
$sessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$sessionMock->method('getVariable')->willReturn('myUserId');
/** @var d3_totp_user|MockObject $userMock */
$userMock = $this->getMockBuilder(User::class)
->onlyMethods(['logout'])
->getMock();
$userMock->expects($doLogout)->method('logout')->willReturn(true);
/** @var Utils|MockObject $utilsMock */
$utilsMock = $this->getMockBuilder(Utils::class)
->onlyMethods(['redirect'])
->getMock();
$utilsMock->expects($doRedirect)->method('redirect')->willReturn(true);
/** @var d3totp|MockObject $totpMock */
$totpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['loadByUserId'])
->getMock();
$totpMock->expects($this->atLeastOnce())->method('loadByUserId')->with('myUserId')->willReturn(1);
/** @var totpSystemEventHandler|MockObject $sut */
$sut = $this->getMockBuilder(SystemEventHandler::class)
->onlyMethods(['d3GetTotpObject', 'd3TotpGetSession', 'd3TotpLoginMissing',
'd3TotpGetUserObject', 'getUtilsObject', ])
->getMock();
$sut->method('d3GetTotpObject')->willReturn($totpMock);
$sut->method('d3TotpGetSession')->willReturn($sessionMock);
$sut->method('d3TotpLoginMissing')->with($totpMock)->willReturn($totpMissing);
$sut->method('d3TotpGetUserObject')->willReturn($userMock);
$sut->method('getUtilsObject')->willReturn($utilsMock);
$this->callMethod(
$sut,
'd3requestTotp'
);
}
/**
* @return array
*/
public function canRequestTotpDataProvider(): array
{
return [
'no totp missing' => [false, $this->never(), $this->never()],
'totp missing' => [true, $this->once(), $this->once()],
];
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::d3GetTotpObject
*/
public function canGetTotpObject()
{
/** @var totpSystemEventHandler $sut */
$sut = oxNew(SystemEventHandler::class);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod(
$sut,
'd3GetTotpObject'
)
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::getUtilsObject
*/
public function canGetUtilsObject()
{
/** @var totpSystemEventHandler $sut */
$sut = oxNew(SystemEventHandler::class);
$this->assertInstanceOf(
Utils::class,
$this->callMethod(
$sut,
'getUtilsObject'
)
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::d3TotpGetSession
*/
public function canGetSessionObject()
{
/** @var totpSystemEventHandler $sut */
$sut = oxNew(SystemEventHandler::class);
$this->assertInstanceOf(
Session::class,
$this->callMethod(
$sut,
'd3TotpGetSession'
)
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::d3TotpGetUserObject
*/
public function canGetUserObject()
{
/** @var totpSystemEventHandler $sut */
$sut = oxNew(SystemEventHandler::class);
$this->assertInstanceOf(
User::class,
$this->callMethod(
$sut,
'd3TotpGetUserObject'
)
);
}
/**
* @test
* @param $isActive
* @param $hasTotpAuth
* @param $expected
* @return void
* @throws ReflectionException
* @dataProvider checkTotpLoginMissingDataProvider
* @covers \D3\Totp\Modules\Core\totpSystemEventHandler::d3TotpLoginMissing
*/
public function checkTotpLoginMissing($isActive, $hasTotpAuth, $expected)
{
/** @var Session|MockObject $sessionMock */
$sessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['getVariable'])
->getMock();
$sessionMock->method('getVariable')->willReturn($hasTotpAuth);
/** @var d3totp|MockObject $totpMock */
$totpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['isActive'])
->getMock();
$totpMock->method('isActive')->willReturn($isActive);
/** @var totpSystemEventHandler|MockObject $sut */
$sut = $this->getMockBuilder(SystemEventHandler::class)
->onlyMethods(['d3TotpGetSession'])
->getMock();
$sut->method('d3TotpGetSession')->willReturn($sessionMock);
$this->assertSame(
$expected,
$this->callMethod(
$sut,
'd3TotpLoginMissing',
[$totpMock]
)
);
}
/**
* @return array
*/
public function checkTotpLoginMissingDataProvider(): array
{
return [
'totp not active' => [false, false, false],
'missing totp' => [true, false, true],
'totp exists' => [true, true, false],
];
}
}

View File

@ -10,10 +10,12 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use OxidEsales\TestingLibrary\UnitTestCase;
abstract class d3TotpUnitTestCase extends d3ModCfgUnitTestCase
abstract class d3TotpUnitTestCase extends UnitTestCase
{
}