Compare commits

..

2 Commits

Author SHA1 Message Date
b725333e86
add webauthn assertion 2022-10-23 22:42:32 +02:00
911ff99c83
add webauthn registration and save it to database 2022-10-22 23:28:39 +02:00
69 changed files with 1972 additions and 2536 deletions

View File

@ -4,19 +4,7 @@ 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.1.1.0...rel_2.x)
## [2.1.1.0](https://git.d3data.de/D3Public/oxtotp/compare/2.1.0.0...2.1.1.0) - 2023-09-07
- add product logo
- installable in OXID 6.5.2 + 6.5.3
## [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
- Further protection of the login
## [Unreleased](https://git.d3data.de/D3Public/oxtotp/compare/2.0.0.0...rel_2.x)
## [2.0.0.0](https://git.d3data.de/D3Public/oxtotp/compare/1.1.0.0...2.0.0.0) - 2022-09-30
### Added

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 (one-time password) for OXID eShop
# 2-factor authentication (TOTP) for OXID eShop
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.
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.
## 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.png "Login Backend")
![Login Frontend](assets/login_frontend.png "Login Frontend")
![Login Backend](assets/login_backend.jpg "Login Backend")
![Login Frontend](assets/login_frontend.jpg "Login Frontend")
## System requirements
@ -33,14 +33,10 @@ 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/oxid-twofactor-onetimepassword
composer require d3/oxtotp
```
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 (Einmalpasswort) fĂĽr OXID eShop
# 2-Faktor-Authentisierung (TOTP) fĂĽr OXID eShop
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.
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.
## 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.png "Login Backend")
![Login Frontend](assets/login_frontend.png "Login Frontend")
![Login Backend](assets/login_backend.jpg "Login Backend")
![Login Frontend](assets/login_frontend.jpg "Login Frontend")
## Systemanforderungen
@ -33,14 +33,10 @@ 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/oxid-twofactor-onetimepassword
composer require d3/oxtotp
```
Aktivieren Sie das Modul im Shopadmin unter "Erweiterungen -> Module".

BIN
assets/login_backend.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

BIN
assets/login_frontend.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,5 +1,5 @@
{
"name": "d3/oxid-twofactor-onetimepassword",
"name": "d3/oxtotp",
"description": "Two-factor authentication via time-based one-time password for OXID eSales shop",
"type": "oxideshop-module",
"keywords": [
@ -7,13 +7,7 @@
"modules",
"eShop",
"d3",
"2FA",
"two factor",
"second factor",
"TOTP",
"OTP",
"one-time password",
"authenticator"
"2FA"
],
"authors": [
{
@ -46,28 +40,24 @@
"php": ">=7.2",
"ext-xmlwriter": "*",
"ext-openssl": "*",
"oxid-esales/oxideshop-ce": "6.8.0 - 6.14",
"oxid-esales/oxideshop-ce": "6.8.0 - 6.12",
"spomky-labs/otphp": "^10.0 || ^11.0",
"bacon/bacon-qr-code": "^2.0",
"laminas/laminas-math": "^3.2",
"d3/testingtools": "^1.0"
"web-auth/webauthn-lib": "^3.3",
"nyholm/psr7": "^1.5.1",
"nyholm/psr7-server": "^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,10 +5,4 @@ parameters:
paths:
- src
level: 5
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
phpVersion: 70300

View File

@ -4,7 +4,6 @@ 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;
@ -14,7 +13,7 @@ class d3force_2fa extends d3user_totp
{
$this->addTplParam('force2FA', true);
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$userID = $this->d3GetSessionObject()->getVariable("auth");
$this->_sEditObjectId = $userID;
return parent::render();
@ -23,7 +22,7 @@ class d3force_2fa extends d3user_totp
protected function _authorize()
{
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$userID = $this->d3GetSessionObject()->getVariable("auth");
return ($this->d3IsAdminForce2FA() && !empty($userID));
}
@ -31,7 +30,7 @@ class d3force_2fa extends d3user_totp
/**
* @return Session
*/
private function d3TotpGetSessionObject()
private function d3GetSessionObject()
{
return Registry::getSession();
}

View File

@ -1,234 +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
*/
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,6 +35,12 @@ 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;
@ -108,7 +114,7 @@ class d3_account_totp extends AccountController
$oTotp->save();
$oTotpBackupCodes->save();
} catch (Exception $oExcp) {
Registry::get(UtilsView::class)->addErrorToDisplay($oExcp->getMessage());
Registry::get(UtilsView::class)->addErrorToDisplay($oExcp);
}
}
}

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

View File

@ -0,0 +1,117 @@
<?php
namespace D3\Totp\Application\Model\Webauthn;
use Doctrine\DBAL\Query\QueryBuilder;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
class PublicKeyCredentials extends BaseModel implements PublicKeyCredentialSourceRepository
{
protected $_sCoreTable = 'd3wa_usercredentials';
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource
{
/** @var QueryBuilder $qb */
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
$qb->select('credential')
->from($this->getViewName())
->where(
$qb->expr()->and(
$qb->expr()->eq(
'credid_hex',
$qb->createNamedParameter(bin2hex($publicKeyCredentialId))
),
$qb->expr()->eq(
'oxshopid',
$qb->createNamedParameter(Registry::getConfig()->getShopId())
)
)
);
$credential = $qb->execute()->fetchOne();
if (!strlen($credential)) {
return null;
}
$credential = unserialize(hex2bin($credential));
return $credential instanceof PublicKeyCredentialSource ? $credential : null;
}
public function getIdByCredentialId(string $publicKeyCredentialId): ?string
{
/** @var QueryBuilder $qb */
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
$qb->select('oxid')
->from($this->getViewName())
->where(
$qb->expr()->and(
$qb->expr()->eq(
'credid_hex',
$qb->createNamedParameter(bin2hex($publicKeyCredentialId))
),
$qb->expr()->eq(
'oxshopid',
$qb->createNamedParameter(Registry::getConfig()->getShopId())
)
)
);
$oxid = $qb->execute()->fetchOne();
return strlen($oxid) ? $oxid : null;
}
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array
{
/** @var QueryBuilder $qb */
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
$qb->select('credential')
->from($this->getViewName())
->where(
$qb->expr()->and(
$qb->expr()->eq(
'oxuserid',
$qb->createNamedParameter($publicKeyCredentialUserEntity->getId())
),
$qb->expr()->eq(
'oxshopid',
$qb->createNamedParameter(Registry::getConfig()->getShopId())
)
)
);
// generate decoded credentials list
return array_map(function (array $fields) {
return unserialize(hex2bin($fields['credential']));
}, $qb->execute()->fetchAllAssociative());
}
/**
* @param PublicKeyCredentialSource $publicKeyCredentialSource
* @return void
* @throws \Exception
*/
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource): void
{
// will saved on every successfully assertion, set id to prevent duplicated database entries
$id = $this->getIdByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
$this->setId($id);
$this->assign([
'oxshopid' => Registry::getConfig()->getShopId(),
'oxuserid' => $publicKeyCredentialSource->getUserHandle(),
'credid_bin' => $publicKeyCredentialSource->getPublicKeyCredentialId(),
'credid_hex' => bin2hex($publicKeyCredentialSource->getPublicKeyCredentialId()),
'pubkey_bin' => $publicKeyCredentialSource->getCredentialPublicKey(),
'pubkey_hex' => bin2hex($publicKeyCredentialSource->getCredentialPublicKey()),
'credential' => bin2hex(serialize($publicKeyCredentialSource)),
]);
$this->save();
}
}

View File

@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace D3\Totp\Application\Model\Webauthn;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRequestOptions;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\Server;
class Webauthn
{
public const SESSION_CREATIONS_OPTIONS = 'd3WebAuthnCreationOptions';
public const SESSION_ASSERTION_OPTIONS = 'd3WebAuthnAssertionOptions';
public function getCreationOptions()
{
/** @var d3_totp_user $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
$credentialSourceRepository = new PublicKeyCredentials();
$credentialSources = $credentialSourceRepository->findAllForUserEntity($userEntity);
$excludeCredentials = array_map(function (PublicKeyCredentialSource $credential) {
return $credential->getPublicKeyCredentialDescriptor();
}, $credentialSources);
$server = $this->getServer();
$publicKeyCredentialCreationOptions = $server->generatePublicKeyCredentialCreationOptions(
$userEntity,
PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
$excludeCredentials
);
Registry::getSession()->setVariable(self::SESSION_CREATIONS_OPTIONS, $publicKeyCredentialCreationOptions);
return json_encode($publicKeyCredentialCreationOptions);
}
public function getRequestOptions()
{
/** @var d3_totp_user $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
// Get the list of authenticators associated to the user
$credentialSourceRepository = oxNew(PublicKeyCredentials::class);
$credentialSources = $credentialSourceRepository->findAllForUserEntity($userEntity);
// Convert the Credential Sources into Public Key Credential Descriptors
$allowedCredentials = array_map(function (PublicKeyCredentialSource $credential) {
return $credential->getPublicKeyCredentialDescriptor();
}, $credentialSources);
$server = $this->getServer();
// We generate the set of options.
$publicKeyCredentialRequestOptions = $server->generatePublicKeyCredentialRequestOptions(
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value
$allowedCredentials
);
Registry::getSession()->setVariable(self::SESSION_ASSERTION_OPTIONS, $publicKeyCredentialRequestOptions);
return json_encode($publicKeyCredentialRequestOptions);
}
/**
* @return Server
*/
public function getServer()
{
$rpEntity = new PublicKeyCredentialRpEntity(
Registry::getConfig()->getActiveShop()->getFieldData('oxname'),
preg_replace('/(^www\.)(.*)/mi', '$2', $_SERVER['HTTP_HOST'])
);
return new Server($rpEntity, new PublicKeyCredentials());
}
public function saveAuthn(string $credential)
{
try {
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory,
$psr17Factory,
$psr17Factory,
$psr17Factory
);
$serverRequest = $creator->fromGlobals();
$publicKeyCredentialSource = $this->getServer()->loadAndCheckAttestationResponse(
html_entity_decode($credential),
Registry::getSession()->getVariable(self::SESSION_CREATIONS_OPTIONS),
$serverRequest
);
dumpvar($publicKeyCredentialSource);
dumpvar(serialize($publicKeyCredentialSource));
dumpvar(unserialize(serialize($publicKeyCredentialSource)));
echo "<hr>";
dumpvar(bin2hex(serialize($publicKeyCredentialSource)));
dumpvar(unserialize(hex2bin(bin2hex(serialize($publicKeyCredentialSource)))));
$pkCredential = oxNew(PublicKeyCredentials::class);
$pkCredential->saveCredentialSource($publicKeyCredentialSource);
} catch (\Exception $e) {
dumpvar($e->getMessage());
dumpvar($e);
die();
}
}
public function assertAuthn(string $response)
{
try {
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory,
$psr17Factory,
$psr17Factory,
$psr17Factory
);
$serverRequest = $creator->fromGlobals();
/** @var d3_totp_user $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
$publicKeySource = $this->getServer()->loadAndCheckAssertionResponse(
html_entity_decode($response),
Registry::getSession()->getVariable(self::SESSION_ASSERTION_OPTIONS),
$userEntity,
$serverRequest
);
/*
dumpvar($publicKeySource);
dumpvar(serialize($publicKeySource));
dumpvar(unserialize(serialize($publicKeySource)));
echo "<hr>";
dumpvar(bin2hex(serialize($publicKeySource)));
dumpvar(unserialize(hex2bin(bin2hex(serialize($publicKeySource)))));
*/
dumpvar('successfully');
} catch (\Exception $e) {
dumpvar($e->getMessage());
dumpvar($e);
die();
}
}
}

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->d3TotpGetUserObject();
$oUser = $this->d3GetUserObject();
$oUser->load($sUserId);
$salt = $oUser->getFieldData('oxpasssalt');
$sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )";
@ -74,9 +74,8 @@ class d3backupcode extends BaseModel
return $this->getUser();
}
/** @var d3_totp_user $oUser */
$sUserId = Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER);
$oUser = oxNew(User::class);
$sUserId = $oUser->d3TotpGetCurrentUser();
$oUser->load($sUserId);
return $oUser;
}
@ -84,7 +83,7 @@ class d3backupcode extends BaseModel
/**
* @return User
*/
public function d3TotpGetUserObject()
public function d3GetUserObject()
{
return oxNew(User::class);
}

View File

@ -29,6 +29,11 @@ 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;
@ -143,7 +148,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'));
}
@ -268,7 +273,7 @@ class d3totp extends BaseModel
}
/**
* @param null|string $oxid
* @param null $oxid
* @return bool
* @throws DatabaseConnectionException
*/

View File

@ -1,30 +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
*/
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,7 +25,6 @@ $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,7 +25,6 @@ $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,7 +41,6 @@ $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,7 +41,6 @@ $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

@ -1,126 +0,0 @@
<!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,12 +1,5 @@
[{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)}]
@ -31,9 +24,6 @@
text-align: right;
color: #6c7c98;
}
.container-fluid {
font-size: 13px;
}
</style>
[{if $force2FA}]
@ -67,124 +57,132 @@
[{/if}]
[{if $oxid && $oxid != '-1'}]
<div class="container-fluid">
<div class="row">
<div class="col-4">
<div class="card">
<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">
[{block name="user_d3user_totp_form1"}]
[{if false == $totp->getId()}]
<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>
<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>
[{elseif $force2FA}]
<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>
<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>
[{else}]
<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>
<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>
[{/if}]
[{/block}]
</div>
</div>
<div class="col-8">
<div class="card">
</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">
[{block name="user_d3user_totp_form2"}]
[{if false == $totp->getId()}]
<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>
<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>
[{else}]
<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>
<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}]
[{/if}]
[{/block}]
</div>
</div>
</div>
</div>
</table>
</td>
<!-- Ende rechte Seite -->
</tr>
</table>
[{/if}]
</form>

View File

@ -1,11 +0,0 @@
[{$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

@ -1,11 +0,0 @@
[{$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,18 @@
[{oxscript include=$oViewConf->getModuleUrl('d3totp', 'out/src/js/index.js')}]
[{capture name="d3script"}]
var creationOptions = [{$requestOptions}];
requestCredentials(creationOptions);
[{/capture}]
[{oxscript add=$smarty.capture.d3script}]
--A--
<form id="webauthn" action="[{$oViewConf->getSelfActionLink()}]" method="post">
[{$oViewConf->getHiddenSid()}]
[{$oViewConf->getNavFormParams()}]
<input type="hidden" name="fnc" value="assertAuthn">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="credential" value='credent'>
</form>
--B--
[{$smarty.block.parent}]

View File

@ -0,0 +1,20 @@
[{* from https://github.com/jcjones/webauthn.bin.coffee *}]
[{oxscript include=$oViewConf->getModuleUrl('d3totp', 'out/src/js/index.js')}]
[{capture name="d3script"}]
var creationOptions = [{$creationOptions}];
createCredentials(creationOptions);
[{/capture}]
[{oxscript add=$smarty.capture.d3script}]
--A--
<form id="webauthn" action="[{$oViewConf->getSelfActionLink()}]" method="post">
[{$oViewConf->getHiddenSid()}]
[{$oViewConf->getNavFormParams()}]
<input type="hidden" name="fnc" value="saveAuthn">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="credential" value='credent'>
</form>
--B--
[{$smarty.block.parent}]

View File

@ -1,4 +0,0 @@
[{$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,56 +11,20 @@
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="d3TotpCheckTotpLogin">
<input type="hidden" name="fnc" value="checkTotplogin">
<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()}]
<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>
<label for="d3totp">[{oxmultilang ident="D3_TOTP_INPUT"}]</label>
<input type="text" name="d3totp" id="d3totp" value="" size="49" autofocus autocomplete="off"><br>
[{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>
[{oxmultilang ident="D3_TOTP_INPUT_HELP"}]
<button type="submit" class="btn btn-primary">
[{oxmultilang ident="D3_TOTP_SUBMIT_LOGIN"}]
@ -70,7 +34,7 @@
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="login" id="login">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="d3TotpCancelTotpLogin">
<input type="hidden" name="fnc" value="cancelTotplogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]

View File

@ -11,8 +11,6 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\Modules\Application\Component
{
@ -26,8 +24,10 @@ namespace D3\Totp\Modules\Application\Component
namespace D3\Totp\Modules\Application\Controller
{
use OxidEsales\Eshop\Application\Controller\ContactController;
use OxidEsales\Eshop\Application\Controller\OrderController;
use OxidEsales\Eshop\Application\Controller\PaymentController;
use OxidEsales\Eshop\Application\Controller\StartController;
use OxidEsales\Eshop\Application\Controller\UserController;
class d3_totp_UserController_parent extends UserController
@ -41,6 +41,14 @@ namespace D3\Totp\Modules\Application\Controller
class d3_totp_OrderController_parent extends OrderController
{
}
class d3_totp_StartController_parent extends StartController
{
}
class d3_totp_ContactController_parent extends ContactController
{
}
}
namespace D3\Totp\Modules\Application\Controller\Admin
@ -67,13 +75,8 @@ 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,64 +16,55 @@ 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;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Core\UtilsView;
class d3_totp_UserComponent extends d3_totp_UserComponent_parent
{
/**
* @param User $oUser
*
* @return string
* @return string|void
* @throws DBALException
* @throws DatabaseConnectionException
*/
protected function _afterLogin($oUser)
public function login_noredirect()
{
if (!$oUser instanceof User) {
throw oxNew(InvalidArgumentException::class, 'user argument must an instance of User class');
}
parent::login_noredirect();
if ($oUser->getId()) {
$oUser = $this->getUser();
if ($oUser instanceof User && $oUser->getId()) {
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($oUser->getId());
if ($totp->isActive()
&& !$this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_AUTH)
&& !Registry::getSession()->getVariable(d3totp::TOTP_SESSION_VARNAME)
) {
$this->d3TotpGetSession()->setVariable(
d3totp_conf::SESSION_CURRENTCLASS,
Registry::getSession()->setVariable(
d3totp::TOTP_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3totplogin' ? $this->getParent()->getClassKey() : 'start'
);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, $oUser->getId());
Registry::getSession()->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->d3TotpGetUtils()->redirect($sUrl, false);
return "d3totplogin";
}
}
return parent::_afterLogin($oUser);
}
/**
* @return d3totp
*/
public function d3GetTotpObject(): d3totp
public function d3GetTotpObject()
{
return oxNew(d3totp::class);
}
@ -82,33 +73,26 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function d3TotpCheckTotpLogin()
public function checkTotplogin()
{
$sTotp = implode('', Registry::getRequest()->getRequestEscapedParameter('d3totp') ?: []);
$sTotp = Registry::getRequest()->getRequestEscapedParameter('d3totp', true);
/** @var d3_totp_user $oUser */
$sUserId = Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER);
$oUser = oxNew(User::class);
$sUserId = Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER);
$oUser->load($sUserId);
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($sUserId);
try {
if (!$this->d3TotpIsNoTotpOrNoLogin($totp) && $this->d3TotpHasValidTotp($sTotp, $totp)) {
// relogin, don't extract from this try block
$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);
if (!$this->isNoTotpOrNoLogin($totp) && $this->hasValidTotp($sTotp, $totp)) {
$this->d3TotpRelogin($oUser, $sTotp);
$this->d3TotpClearSessionVariables();
return false;
}
} catch (d3totp_wrongOtpException $oEx) {
$this->d3TotpGetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3totplogin');
$this->d3GetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3totplogin');
}
return 'd3totplogin';
@ -117,20 +101,12 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
/**
* @return UtilsView
*/
public function d3TotpGetUtilsView()
public function d3GetUtilsView()
{
return Registry::getUtilsView();
}
/**
* @return Utils
*/
public function d3TotpGetUtils()
{
return Registry::getUtils();
}
public function d3TotpCancelTotpLogin()
public function cancelTotpLogin()
{
$this->d3TotpClearSessionVariables();
@ -141,9 +117,9 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @param d3totp $totp
* @return bool
*/
public function d3TotpIsNoTotpOrNoLogin($totp)
public function isNoTotpOrNoLogin($totp)
{
return false == Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER)
return false == Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER)
|| false == $totp->isActive();
}
@ -154,23 +130,38 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @throws DatabaseConnectionException
* @throws d3totp_wrongOtpException
*/
public function d3TotpHasValidTotp($sTotp, $totp)
public function hasValidTotp($sTotp, $totp)
{
return Registry::getSession()->getVariable(d3totp_conf::SESSION_AUTH) ||
$totp->verify($sTotp);
return Registry::getSession()->getVariable(d3totp::TOTP_SESSION_VARNAME) ||
(
$sTotp && $totp->verify($sTotp)
);
}
/**
* @param User $oUser
* @param $sTotp
*/
public function d3TotpRelogin(User $oUser, $sTotp)
{
$this->d3GetSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, $sTotp);
$this->d3GetSession()->setVariable('usr', $oUser->getId());
$this->setUser(null);
$this->setLoginStatus(USER_LOGIN_SUCCESS);
$this->_afterLogin($oUser);
}
public function d3TotpClearSessionVariables()
{
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_CURRENTCLASS);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_CURRENTUSER);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_NAVFORMPARAMS);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTCLASS);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTUSER);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_NAVFORMPARAMS);
}
/**
* @return Session
*/
public function d3TotpGetSession()
public function d3GetSession()
{
return Registry::getSession();
}

View File

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

View File

@ -0,0 +1,23 @@
<?php
namespace D3\Totp\Modules\Application\Controller;
use D3\Totp\Application\Model\Webauthn\Webauthn;
use OxidEsales\Eshop\Core\Registry;
class d3_totp_ContactController extends d3_totp_ContactController_parent
{
public function render()
{
$webAuthn = oxNew(Webauthn::class);
$this->addTplParam('requestOptions', $webAuthn->getRequestOptions());
return parent::render();
}
public function assertAuthn()
{
$webAuthn = oxNew(Webauthn::class);
$webAuthn->assertAuthn(Registry::getRequest()->getRequestEscapedParameter('credential'));
}
}

View File

@ -18,6 +18,4 @@ 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,6 +18,4 @@ 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

@ -0,0 +1,23 @@
<?php
namespace D3\Totp\Modules\Application\Controller;
use D3\Totp\Application\Model\Webauthn\Webauthn;
use OxidEsales\Eshop\Core\Registry;
class d3_totp_StartController extends d3_totp_StartController_parent
{
public function render()
{
$webAuthn = oxNew(Webauthn::class);
$this->addTplParam('creationOptions', $webAuthn->getCreationOptions());
return parent::render();
}
public function saveAuthn()
{
$webAuthn = oxNew(Webauthn::class);
$webAuthn->saveAuthn(Registry::getRequest()->getRequestEscapedParameter('credential'));
}
}

View File

@ -18,6 +18,4 @@ 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,9 +15,7 @@ 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;
@ -25,31 +23,26 @@ use OxidEsales\Eshop\Core\Session;
trait d3_totp_getUserTrait
{
use IsMockable;
/**
* @return bool|object|User
* @throws DatabaseConnectionException
*/
public function getUser()
{
/** @var User|null $user */
$user = $this->d3CallMockableFunction([$this->parentClass, 'getUser']);
$oUser = parent::getUser();
if ($user && $user->isLoaded() && $user->getId()) {
if ($oUser instanceof User && $oUser->getId()) {
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($user->getId());
$totp->loadByUserId($oUser->getId());
if ($totp->isActive()
&& !$this->d3TotpGetSessionObject()->getVariable(
isAdmin() ? d3totp_conf::SESSION_ADMIN_AUTH : d3totp_conf::SESSION_AUTH
)
&& !$this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME)
) {
return false;
}
}
return $user;
return $oUser;
}
/**
@ -63,7 +56,7 @@ trait d3_totp_getUserTrait
/**
* @return Session
*/
public function d3TotpGetSessionObject()
public function d3GetSessionObject()
{
return Registry::getSession();
}

View File

@ -16,9 +16,10 @@ 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\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use Webauthn\PublicKeyCredentialUserEntity;
class d3_totp_user extends d3_totp_user_parent
{
@ -26,10 +27,7 @@ class d3_totp_user extends d3_totp_user_parent
{
$return = parent::logout();
$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);
$this->d3GetSession()->deleteVariable(d3totp::TOTP_SESSION_VARNAME);
return $return;
}
@ -45,23 +43,24 @@ class d3_totp_user extends d3_totp_user_parent
/**
* @return Session
*/
public function d3TotpGetSession()
public function d3GetSession()
{
return Registry::getSession();
}
/**
* @return string|null
* @return PublicKeyCredentialUserEntity
*/
public function d3TotpGetCurrentUser(): ?string
public function d3GetWebauthnUserEntity(): PublicKeyCredentialUserEntity
{
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));
if ($this->isLoaded()) {
return oxNew(PublicKeyCredentialUserEntity::class,
$this->getFieldData('oxusername'),
$this->getId(),
$this->getFieldData('oxfname') . ' ' . $this->getFieldData('oxlname')
);
}
throw oxNew(StandardException::class, 'can not create webauthn user entity from not loaded user');
}
}
}

View File

@ -16,7 +16,6 @@ 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;
@ -33,9 +32,10 @@ class d3_totp_utils extends d3_totp_utils_parent
public function checkAccessRights()
{
$blAuth = parent::checkAccessRights();
$blAuth = $this->d3AuthHook($blAuth);
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$totpAuth = (bool) $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);
$userID = $this->d3GetSessionObject()->getVariable("auth");
$totpAuth = (bool) $this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME);
/** @var d3totp $totp */
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($userID);
@ -47,11 +47,21 @@ 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=d3totpadminlogin', false);
$this->redirect('index.php?cl=login');
if (false == defined('OXID_PHP_UNIT')) {
// @codeCoverageIgnoreStart
exit;
// @codeCoverageIgnoreEnd
}
}
return $blAuth;
@ -60,7 +70,7 @@ class d3_totp_utils extends d3_totp_utils_parent
/**
* @return Session
*/
public function d3TotpGetSessionObject()
public function d3GetSessionObject()
{
return Registry::getSession();
}

View File

@ -1,101 +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
*/
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);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

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

@ -13,26 +13,27 @@
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;
use D3\Totp\Application\Controller\d3totplogin;
use D3\Totp\Modules\Application\Component\d3_totp_UserComponent;
use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController;
use D3\Totp\Modules\Application\Controller\d3_totp_ContactController;
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_StartController;
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\ContactController;
use OxidEsales\Eshop\Application\Controller\OrderController;
use OxidEsales\Eshop\Application\Controller\PaymentController;
use OxidEsales\Eshop\Application\Controller\StartController;
use OxidEsales\Eshop\Application\Controller\UserController;
use OxidEsales\Eshop\Core\SystemEventHandler;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Application\Model as OxidModel;
@ -50,18 +51,17 @@ $logo = '<img src="https://logos.oxidmodule.com/d3logo.svg" alt="(D3)" style="he
$aModule = [
'id' => $sModuleId,
'title' => [
'de' => $logo . ' zweiter Faktor - Einmalpasswort',
'en' => $logo . ' second factor - one-time password',
'de' => $logo . ' Zwei-Faktor-Authentisierung',
'en' => $logo . ' two-factor authentication',
],
'description' => [
'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',
'de' => 'Zwei-Faktor-Authentisierung (TOTP) f&uuml;r OXID eSales Shop',
'en' => 'Two-factor authentication (TOTP) for OXID eSales shop',
],
'version' => '2.1.1.0',
'version' => '2.0.0.0',
'author' => 'D&sup3; Data Development (Inh.: Thomas Dartsch)',
'email' => 'support@shopmodule.com',
'url' => 'https://www.oxidmodule.com/',
'thumbnail' => 'logo.png',
'extend' => [
UserController::class => d3_totp_UserController::class,
PaymentController::class => d3_totp_PaymentController::class,
@ -70,20 +70,19 @@ $aModule = [
LoginController::class => d3_totp_LoginController::class,
Utils::class => d3_totp_utils::class,
UserComponent::class => d3_totp_UserComponent::class,
SystemEventHandler::class => totpSystemEventHandler::class,
StartController::class => d3_totp_StartController::class,
ContactController::class => d3_totp_ContactController::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' => [
[
@ -109,20 +108,14 @@ $aModule = [
'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',
'template' => 'page/shop/start.tpl',
'block' => 'start_welcome_text',
'file' => 'Application/views/blocks/page/shop/start_welcome_text.tpl',
],
[
'theme' => 'flow',
'template' => 'page/account/dashboard.tpl',
'block' => 'account_dashboard_col2',
'file' => 'Application/views/blocks/page/account/account_dashboard_col2_flow.tpl',
'template' => 'page/info/contact.tpl',
'block' => 'd3webauthn',
'file' => 'Application/views/blocks/page/info/d3webauthn.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,33 +9,4 @@
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,35 +14,4 @@
.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;
}

185
src/out/src/js/index.js Normal file
View File

@ -0,0 +1,185 @@
if (!window.PublicKeyCredential) {
console.error('no window pubkeycred available');
}
const base64UrlDecode = (input) => {
"use strict";
input = input
.replace(/-/g, '+')
.replace(/_/g, '/');
const pad = input.length % 4;
if (pad) {
if (pad === 1) {
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
}
input += new Array(5-pad).join('=');
}
return window.atob(input);
};
const prepareOptions = (publicKey) => {
"use strict";
//Convert challenge from Base64Url string to Uint8Array
publicKey.challenge = Uint8Array.from(
base64UrlDecode(publicKey.challenge),
c => c.charCodeAt(0)
);
//Convert the user ID from Base64 string to Uint8Array
if (publicKey.user !== undefined) {
publicKey.user = {
...publicKey.user,
id: Uint8Array.from(
window.atob(publicKey.user.id),
c => c.charCodeAt(0)
),
};
}
//If excludeCredentials is defined, we convert all IDs to Uint8Array
if (publicKey.excludeCredentials !== undefined) {
publicKey.excludeCredentials = publicKey.excludeCredentials.map(
data => {
return {
...data,
id: Uint8Array.from(
base64UrlDecode(data.id),
c => c.charCodeAt(0)
),
};
}
);
}
if (publicKey.allowCredentials !== undefined) {
publicKey.allowCredentials = publicKey.allowCredentials.map(
data => {
return {
...data,
id: Uint8Array.from(
base64UrlDecode(data.id),
c => c.charCodeAt(0)
),
};
}
);
}
return publicKey;
};
/** https://gist.github.com/jonleighton/958841 **/
function base64ArrayBuffer(arrayBuffer) {
"use strict";
var base64 = ''
var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
var bytes = new Uint8Array(arrayBuffer)
var byteLength = bytes.byteLength
var byteRemainder = byteLength % 3
var mainLength = byteLength - byteRemainder
var a, b, c, d
var chunk
// Main loop deals with bytes in chunks of 3
for (var i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
d = chunk & 63 // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
}
// Deal with the remaining bytes and padding
if (byteRemainder === 1) {
chunk = bytes[mainLength]
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1
base64 += encodings[a] + encodings[b] + '=='
} else if (byteRemainder === 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1
base64 += encodings[a] + encodings[b] + encodings[c] + '='
}
return base64
}
const createCredentials = (publicKey) => {
"use strict";
prepareOptions(publicKey);
navigator.credentials.create({publicKey: publicKey})
.then(function (newCredentialInfo) {
// Send new credential info to server for verification and registration.
var cred = {
id: newCredentialInfo.id,
rawId: base64ArrayBuffer(newCredentialInfo.rawId),
response: {
clientDataJSON: base64ArrayBuffer(newCredentialInfo.response.clientDataJSON),
attestationObject: base64ArrayBuffer(newCredentialInfo.response.attestationObject)
},
type: newCredentialInfo.type
};
document.getElementById('webauthn').credential.value = JSON.stringify(cred);
document.getElementById('webauthn').submit();
}).catch(function (err) {
console.log('--2--');
console.log('WebAuthn create: ' + err);
// No acceptable authenticator or user refused consent. Handle appropriately.
});
}
const requestCredentials = (publicKey) => {
"use strict";
console.log('--AB--');
prepareOptions(publicKey);
navigator.credentials.get({publicKey: publicKey})
.then(function (authenticateInfo) {
console.log(authenticateInfo);
// Send authenticate info to server for verification.
var cred = {
id: authenticateInfo.id,
rawId: base64ArrayBuffer(authenticateInfo.rawId),
response: {
authenticatorData: base64ArrayBuffer(authenticateInfo.response.authenticatorData),
signature: base64ArrayBuffer(authenticateInfo.response.signature),
userHandle: authenticateInfo.response.userHandle,
clientDataJSON: base64ArrayBuffer(authenticateInfo.response.clientDataJSON)
},
type: authenticateInfo.type
};
console.log(cred);
document.getElementById('webauthn').credential.value = JSON.stringify(cred);
document.getElementById('webauthn').submit();
}).catch(function (err) {
console.log('--2--');
console.log('WebAuthn auth: ' + err);
// No acceptable authenticator or user refused consent. Handle appropriately.
});
}

View File

@ -0,0 +1,37 @@
<?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

@ -0,0 +1,14 @@
<?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,7 +3,6 @@
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;
@ -28,7 +27,7 @@ class d3force_2faTest extends d3user_totpTest
{
$expected = 'fixture';
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, $expected);
Registry::getSession()->setVariable('auth', $expected);
$this->callMethod(
$this->_oController,
@ -65,7 +64,7 @@ class d3force_2faTest extends d3user_totpTest
Registry::getConfig()->setConfigParam('D3_TOTP_ADMIN_FORCE_2FA', $force2FA);
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, $givenUserId);
Registry::getSession()->setVariable('auth', $givenUserId);
$this->assertSame(
$expected,
@ -93,7 +92,7 @@ class d3force_2faTest extends d3user_totpTest
* @test
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3TotpGetSessionObject
* @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3GetSessionObject
*/
public function testD3GetSessionObject()
{
@ -101,7 +100,7 @@ class d3force_2faTest extends d3user_totpTest
Session::class,
$this->callMethod(
$this->_oController,
'd3TotpGetSessionObject'
'd3GetSessionObject'
)
);
}

View File

@ -1,654 +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
*/
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,11 +11,8 @@
* @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;
@ -27,8 +24,6 @@ use ReflectionException;
class d3user_totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3user_totp */
protected $_oController;
@ -84,7 +79,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canRenderSelectedUser()
{
/** @var User|MockObject $oUserMock */
/** @var User|MockObject $oControllerMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods([
'getId',
@ -123,7 +118,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canRenderUnloadableUser()
{
/** @var User|MockObject $oUserMock */
/** @var User|MockObject $oControllerMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods([
'getId',
@ -219,13 +214,13 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function cantSaveBecauseOfNotVerifiable()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['save'])
->getMock();
$oBackupCodeListMock->expects($this->never())->method('save')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
/** @var d3totp|MockObject $oControllerMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -269,13 +264,13 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function cantSaveBecauseExistingRegistration()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['save'])
->getMock();
$oBackupCodeListMock->expects($this->never())->method('save')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
/** @var d3totp|MockObject $oControllerMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods([
@ -319,7 +314,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canSave()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods([
'save',
@ -329,7 +324,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
$oBackupCodeListMock->expects($this->once())->method('save')->willReturn(true);
$oBackupCodeListMock->method('generateBackupCodes')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
/** @var d3totp|MockObject $oControllerMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -378,7 +373,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
];
$_GET['editval'] = $aEditval;
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods([
'save',
@ -388,7 +383,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
$oBackupCodeListMock->expects($this->once())->method('save')->willReturn(true);
$oBackupCodeListMock->method('generateBackupCodes')->willReturn(true);
/** @var d3totp|MockObject $oTotpMock */
/** @var d3totp|MockObject $oControllerMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods([
'load',
@ -514,7 +509,7 @@ class d3user_totpTest extends d3TotpUnitTestCase
*/
public function canGetAvailableBackupCodeCount()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();

View File

@ -11,11 +11,8 @@
* @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;
@ -27,8 +24,6 @@ use ReflectionException;
class d3_account_totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_account_totp */
protected $_oController;
@ -88,7 +83,7 @@ class d3_account_totpTest extends d3TotpUnitTestCase
*/
public function renderReturnsLoginTemplateIfNotLoggedIn()
{
$oUser = null;
$oUser = false;
/** @var d3_account_totp|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_account_totp::class)
@ -148,7 +143,7 @@ class d3_account_totpTest extends d3TotpUnitTestCase
*/
public function canGetAvailableBackupCodeCount()
{
/** @var d3backupcodelist|MockObject $oBackupCodeListMock */
/** @var d3backupcodelist|MockObject $oControllerMock */
$oBackupCodeListMock = $this->getMockBuilder(d3backupcodelist::class)
->onlyMethods(['getAvailableCodeCount'])
->getMock();

View File

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

View File

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

View File

@ -11,11 +11,8 @@
* @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;
@ -28,8 +25,6 @@ use ReflectionException;
class d3backupcodelistTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3backupcodelist */
protected $_oModel;
@ -57,7 +52,7 @@ class d3backupcodelistTest extends d3TotpUnitTestCase
*/
public function generateBackupCodes()
{
/** @var FrontendController|MockObject $oViewMock */
/** @var FrontendController|MockObject $oConfigMock */
$oViewMock = $this->getMockBuilder(FrontendController::class)
->addMethods(['setBackupCodes'])
->getMock();

View File

@ -11,12 +11,9 @@
* @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;
@ -32,8 +29,6 @@ use stdClass;
class d3totpTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3totp */
protected $_oModel;
@ -867,7 +862,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,51 +11,59 @@
* @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;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Core\UtilsView;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class d3_totp_UserComponentTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @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);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login_noredirect
*/
public function afterLoginFailsIfNoUserLoggedIn()
public function login_noredirectFailsIfNoUserLoggedIn()
{
$oUser = false;
/** @var Utils|MockObject $oUtilsMock */
$oUtilsMock = $this->getMockBuilder(Utils::class)
->onlyMethods(['redirect'])
->getMock();
$oUtilsMock->expects($this->never())->method('redirect')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
/** @var BaseController|MockObject $oParentMock */
$oParentMock = $this->getMockBuilder(BaseController::class)
->addMethods(['isEnabledPrivateSales'])
->getMock();
$oSessionMock->expects($this->never())->method('setVariable');
$oParentMock->method('isEnabledPrivateSales')->willReturn(false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -67,28 +75,28 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3TotpGetSession',
'd3TotpGetUtils',
'getParent',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUser);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('getParent')->willReturn($oParentMock);
$this->expectException(InvalidArgumentException::class);
$_GET['lgn_usr'] = 'username';
$this->callMethod($oControllerMock, '_afterLogin', [$oUser]);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'login_noredirect');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
* @dataProvider afterLoginFailTotpNotActiveOrAlreadyCheckedDataProvider
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login_noredirect
*/
public function afterLoginFailTotpNotActiveOrAlreadyChecked($isActive, $checked)
public function login_noredirectFailTotpNotActive()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
@ -100,18 +108,11 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
$oUserMock->expects($this->never())->method('logout')->willReturn(false);
$oUserMock->method('getId')->willReturn('foo');
/** @var Utils|MockObject $oUtilsMock */
$oUtilsMock = $this->getMockBuilder(Utils::class)
->onlyMethods(['redirect'])
->getMock();
$oUtilsMock->expects($this->never())->method('redirect')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable', 'getVariable'])
/** @var BaseController|MockObject $oParentMock */
$oParentMock = $this->getMockBuilder(BaseController::class)
->addMethods(['isEnabledPrivateSales'])
->getMock();
$oSessionMock->expects($this->never())->method('setVariable');
$oSessionMock->method('getVariable')->willReturn($checked);
$oParentMock->method('isEnabledPrivateSales')->willReturn(false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -121,42 +122,34 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
])
->disableOriginalConstructor()
->getMock();
$oTotpMock->expects($this->once())->method('isActive')->willReturn($isActive);
$oTotpMock->expects($this->once())->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3TotpGetSession',
'd3TotpGetUtils',
'getParent',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUserMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$oControllerMock->method('getParent')->willReturn($oParentMock);
$this->callMethod($oControllerMock, '_afterLogin', [$oUserMock]);
}
$_GET['lgn_usr'] = 'username';
/**
* @return array
*/
public function afterLoginFailTotpNotActiveOrAlreadyCheckedDataProvider(): array
{
return [
'TOTP not active, not checked' => [false, null],
'TOTP not active, checked' => [false, true],
'TOTP active, checked' => [true, true],
];
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'login_noredirect');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::_afterLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::login_noredirect
*/
public function afterLoginPass()
public function login_noredirectPass()
{
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
@ -168,24 +161,11 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
$oUserMock->expects($this->once())->method('logout')->willReturn(false);
$oUserMock->method('getId')->willReturn('foo');
/** @var Utils|MockObject $oUtilsMock */
$oUtilsMock = $this->getMockBuilder(Utils::class)
->onlyMethods(['redirect'])
->getMock();
$oUtilsMock->expects($this->once())->method('redirect')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable', 'getVariable'])
->getMock();
$oSessionMock->expects($this->atLeast(3))->method('setVariable');
$oSessionMock->method('getVariable')->willReturn(null);
/** @var BaseController|MockObject $oParentMock */
$oParentMock = $this->getMockBuilder(BaseController::class)
->onlyMethods(['getClassKey'])
->addMethods(['isEnabledPrivateSales'])
->getMock();
$oParentMock->method('getClassKey')->willReturn('foo');
$oParentMock->method('isEnabledPrivateSales')->willReturn(false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -201,18 +181,23 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'getUser',
'd3GetTotpObject',
'd3TotpGetSession',
'd3TotpGetUtils',
'getParent',
])
->getMock();
$oControllerMock->method('getUser')->willReturn($oUserMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('getParent')->willReturn($oParentMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3TotpGetUtils')->willReturn($oUtilsMock);
$this->callMethod($oControllerMock, '_afterLogin', [$oUserMock]);
$_GET['lgn_usr'] = 'username';
$this->_oController = $oControllerMock;
$this->assertSame(
'd3totplogin',
$this->callMethod($this->_oController, 'login_noredirect')
);
}
/**
@ -222,19 +207,16 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
*/
public function d3GetTotpObjectReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod($oController, 'd3GetTotpObject')
$this->callMethod($this->_oController, 'd3GetTotpObject')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
*/
public function checkTotploginNoTotpLogin()
{
@ -245,36 +227,32 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
->getMock();
$oSessionMock->expects($this->never())->method('setVariable');
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3TotpRelogin',
'd3GetTotpObject',
'd3TotpGetSession',
])
->getMock();
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->expects($this->never())->method('d3TotpHasValidTotp')->willReturn(false);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->expects($this->never())->method('hasValidTotp')->willReturn(false);
$oControllerMock->expects($this->never())->method('d3TotpRelogin')->willReturn(false);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertSame(
'd3totplogin',
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
$this->callMethod($this->_oController, 'checkTotplogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
*/
public function checkTotploginUnvalidTotp()
{
@ -285,13 +263,7 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
->getMock();
$oSessionMock->expects($this->never())->method('setVariable');
/** @var d3totp_wrongOtpException|MockObject $oTotpExceptionMock */
/** @var d3totp_wrongOtpException|MockObject $oUtilsViewMock */
$oTotpExceptionMock = $this->getMockBuilder(d3totp_wrongOtpException::class)
->disableOriginalConstructor()
->getMock();
@ -305,29 +277,31 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'd3TotpGetUtilsView',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3TotpRelogin',
'd3GetUtilsView',
'd3GetTotpObject',
'd3TotpGetSession',
])
->getMock();
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('d3TotpHasValidTotp')->willThrowException($oTotpExceptionMock);
$oControllerMock->method('d3TotpGetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('hasValidTotp')->willThrowException($oTotpExceptionMock);
$oControllerMock->expects($this->never())->method('d3TotpRelogin')->willReturn(false);
$oControllerMock->method('d3GetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->assertSame(
'd3totplogin',
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
$this->callMethod($this->_oController, 'checkTotplogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpCheckTotpLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::checkTotplogin
*/
public function checkTotploginValidTotp()
{
@ -338,12 +312,6 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
->getMock();
$oSessionMock->expects($this->atLeast(2))->method('setVariable');
/** @var UtilsView|MockObject $oUtilsViewMock */
$oUtilsViewMock = $this->getMockBuilder(UtilsView::class)
->onlyMethods(['addErrorToDisplay'])
@ -353,64 +321,43 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'd3TotpIsNoTotpOrNoLogin',
'd3TotpHasValidTotp',
'd3TotpGetUtilsView',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3TotpRelogin',
'd3GetUtilsView',
'd3GetTotpObject',
'd3TotpGetSession',
'setLoginStatus',
])
->getMock();
$oControllerMock->method('d3TotpIsNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('d3TotpHasValidTotp')->willReturn(true);
$oControllerMock->method('d3TotpGetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(false);
$oControllerMock->expects($this->once())->method('hasValidTotp')->willReturn(true);
$oControllerMock->expects($this->once())->method('d3TotpRelogin')->willReturn(true);
$oControllerMock->method('d3GetUtilsView')->willReturn($oUtilsViewMock);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->expects($this->once())->method('setLoginStatus')->with(
$this->identicalTo(USER_LOGIN_SUCCESS)
);
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($oControllerMock, 'd3TotpCheckTotpLogin')
$this->callMethod($this->_oController, 'checkTotplogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpGetUtilsView
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3GetUtilsView
*/
public function d3GetUtilsViewReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
UtilsView::class,
$this->callMethod($oController, 'd3TotpGetUtilsView')
$this->callMethod($this->_oController, 'd3GetUtilsView')
);
}
/**
* @test
* @throws ReflectionException
* @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
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::cancelTotpLogin
*/
public function canCancelTotpLogin()
{
@ -420,17 +367,19 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
->getMock();
$oControllerMock->expects($this->once())->method('d3TotpClearSessionVariables')->willReturn(false);
$this->callMethod($oControllerMock, 'd3TotpCancelTotpLogin');
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'cancelTotpLogin');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginTrueNoSessionVariable()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, false);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -439,22 +388,19 @@ 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($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginTrueTotpNotActive()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, true);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -463,22 +409,19 @@ 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($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpIsNoTotpOrNoLogin
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::isNoTotpOrNoLogin
*/
public function isNoTotpOrNoLoginFalse()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_CURRENTUSER, true);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -487,22 +430,19 @@ 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($oController, 'd3TotpIsNoTotpOrNoLogin', [$oTotpMock])
$this->callMethod($this->_oController, 'isNoTotpOrNoLogin', [$oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
*/
public function hasValidTotpTrueSessionVarname()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, true);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, true);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -511,22 +451,19 @@ 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($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
*/
public function hasValidTotpTrueValidTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -535,45 +472,40 @@ 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($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
*/
public function hasValidTotpFalseMissingTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->onlyMethods(['verify'])
->disableOriginalConstructor()
->getMock();
$oTotpMock->method('verify')->willThrowException(oxNew(d3totp_wrongOtpException::class));
$oTotpMock->method('verify')->willReturn(true);
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->expectException(d3totp_wrongOtpException::class);
$this->callMethod($oController, 'd3TotpHasValidTotp', [null, $oTotpMock]);
$this->assertFalse(
$this->callMethod($this->_oController, 'hasValidTotp', [null, $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpHasValidTotp
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::hasValidTotp
*/
public function hasValidTotpFalseUnverifiedTotp()
{
Registry::getSession()->setVariable(d3totp_conf::SESSION_AUTH, false);
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -582,14 +514,49 @@ 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($oController, 'd3TotpHasValidTotp', ['123456', $oTotpMock])
$this->callMethod($this->_oController, 'hasValidTotp', ['123456', $oTotpMock])
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpRelogin
*/
public function d3TotpReloginPass()
{
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['setVariable'])
->getMock();
$oSessionMock->expects($this->atLeast(2))->method('setVariable')->willReturn(false);
/** @var User|MockObject $oUserMock */
$oUserMock = $this->getMockBuilder(User::class)
->onlyMethods(['getId'])
->getMock();
$oUserMock->method('getId')->willReturn('foo');
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods([
'd3GetSession',
'setUser',
'setLoginStatus',
'_afterLogin',
])
->getMock();
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$oControllerMock->expects($this->once())->method('setUser')->willReturn(false);
$oControllerMock->expects($this->once())->method('setLoginStatus')->willReturn(false);
$oControllerMock->expects($this->once())->method('_afterLogin')->willReturn(false);
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'd3TotpRelogin', [$oUserMock, '123456']);
}
/**
* @test
* @throws ReflectionException
@ -605,26 +572,25 @@ class d3_totp_UserComponentTest extends d3TotpUnitTestCase
/** @var UserComponent|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(UserComponent::class)
->onlyMethods(['d3TotpGetSession'])
->onlyMethods(['d3GetSession'])
->getMock();
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oControllerMock->method('d3GetSession')->willReturn($oSessionMock);
$this->callMethod($oControllerMock, 'd3TotpClearSessionVariables');
$this->_oController = $oControllerMock;
$this->callMethod($this->_oController, 'd3TotpClearSessionVariables');
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3TotpGetSession
* @covers \D3\Totp\Modules\Application\Component\d3_totp_UserComponent::d3GetSession
*/
public function d3GetSessionReturnsRightInstance()
{
/** @var d3_totp_UserComponent $oController */
$oController = oxNew(UserComponent::class);
$this->assertInstanceOf(
Session::class,
$this->callMethod($oController, 'd3TotpGetSession')
$this->callMethod($this->_oController, 'd3GetSession')
);
}
}

View File

@ -11,28 +11,23 @@
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\Totp\tests\unit\Modules\Application\Controller\Admin;
use D3\TestingTools\Development\CanAccessRestricted;
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 Exception;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Language;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsServer;
use OxidEsales\Eshop\Core\UtilsView;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_totp_LoginController */
protected $_oController;
@ -53,6 +48,186 @@ 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
@ -69,13 +244,39 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpGetSession
* @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
*/
public function d3GetSessionReturnsRightObject()
{
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oController, 'd3TotpGetSession')
$this->callMethod($this->_oController, 'd3GetSession')
);
}
@ -83,21 +284,47 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
* @dataProvider checkloginNoTotpDataProvider
*/
public function canChecklogin()
public function checkloginNoTotp($hasLoginCredentials)
{
$fixture = 'returnString';
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['loadByUserId'])
->getMock();
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods(['d3CallMockableFunction'])
->onlyMethods([
'd3GetTotpObject',
'isNoTotpOrNoLogin',
'hasValidTotp',
'hasLoginCredentials',
])
->getMock();
$oControllerMock->method('d3CallMockableFunction')->willReturn($fixture);
$oControllerMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('isNoTotpOrNoLogin')->willReturn(true);
$oControllerMock->method('hasValidTotp')->willReturn(false);
$oControllerMock->method('hasLoginCredentials')->willReturn($hasLoginCredentials);
$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(
$fixture,
'login',
$this->callMethod(
$this->_oController,
'checklogin'
@ -105,202 +332,417 @@ 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 canRunTotpAfterLoginDataProvider(): array
public function checkloginNoTotpDataProvider(): array
{
return [
'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()],
'no totp, no login credentials' => [false],
'no totp, given login credentials' => [true],
];
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3totpAfterLoginSetLanguage
* @dataProvider canRunTotpAfterLoginSetLanguageDataProvider
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
*/
public function canRunTotpAfterLoginSetLanguage($languageId)
public function checkloginInvalidTotp()
{
/** @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'])
/** @var d3totp_wrongOtpException|MockObject $oUtilsViewMock */
$oTotpExceptionMock = $this->getMockBuilder(d3totp_wrongOtpException::class)
->disableOriginalConstructor()
->getMock();
$utilsServerMock->expects($this->once())->method('setOxCookie');
/** @var Language|MockObject $langMock */
$langMock = $this->getMockBuilder(Language::class)
->onlyMethods(['setTplLanguage'])
/** @var UtilsView|MockObject $utilsViewMock */
$utilsViewMock = $this->getMockBuilder(UtilsView::class)
->onlyMethods(['addErrorToDisplay'])
->getMock();
$langMock->expects($this->once())->method('setTplLanguage');
$utilsViewMock->expects($this->once())->method('addErrorToDisplay')->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);
$this->callMethod(
$sut,
'd3totpAfterLoginSetlanguage'
);
}
/**
* @return array
*/
public function canRunTotpAfterLoginSetLanguageDataProvider(): array
{
return [
'existing language' => [0],
'not existing language' => [50],
];
}
/**
* @test
* @param $totpActive
* @param $loggedin
* @param $expected
* @return void
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::d3TotpLoginMissing
* @dataProvider d3TotpLoginMissingTestDataProvider
*/
public function d3TotpLoginMissingTest($totpActive, $loggedin, $expected)
{
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
->disableOriginalConstructor()
->onlyMethods(['isActive'])
->onlyMethods(['loadByUserId'])
->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);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_LoginController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder(d3_totp_LoginController::class)
->onlyMethods([
'd3TotpGetSession',
'd3GetTotpObject',
'isNoTotpOrNoLogin',
'hasValidTotp',
'd3GetUtilsView',
])
->getMock();
$oControllerMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$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(
$expected,
$this->callMethod(
$this->_oController,
'd3TotpLoginMissing',
[$oTotpMock]
'login',
$this->callMethod($this->_oController, 'checklogin')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin
*/
public function checkloginValidTotp()
{
/** @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 '
)
);
}
/**
* @return array
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::getBackupCodeCountMessage
*/
public function d3TotpLoginMissingTestDataProvider(): array
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')
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @dataProvider hasLoginCredentialsDataProvider
* @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::hasLoginCredentials
*/
public function hasLoginCredentials($user, $pass, $expected)
{
$_GET['user'] = $user;
$_GET['pwd'] = $pass;
$this->assertSame(
$expected,
$this->callMethod(
$this->_oController,
'hasLoginCredentials'
)
);
}
/**
* @return array[]
*/
public function hasLoginCredentialsDataProvider(): array
{
return [
'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],
'user only' => ['user', null, false],
'pass only' => [null, 'password', false],
'both' => ['user', 'password', true],
];
}
/**
* @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,12 +11,9 @@
* @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;
@ -25,5 +22,25 @@ 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,8 +11,6 @@
* @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;
@ -23,5 +21,25 @@ 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,8 +11,6 @@
* @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;
@ -23,5 +21,25 @@ 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,15 +11,10 @@
* @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;
@ -27,27 +22,6 @@ 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
@ -57,14 +31,16 @@ trait d3_totp_getUserTestTrait
*/
public function getUserHasNoUser()
{
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
/** @var d3_totp_orderController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods(['d3GetTotpObject'])
->getMock();
$oControllerMock->expects($this->never())->method('d3GetTotpObject');
$this->_oController = $oControllerMock;
$this->assertFalse(
$this->callMethod($oControllerMock, 'getUser')
$this->callMethod($this->_oController, 'getUser')
);
}
@ -77,6 +53,12 @@ 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'])
@ -94,23 +76,24 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(false);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
/** @var d3_totp_orderController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3TotpGetSessionObject',
'd3GetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$this->assertSame(
$this->userFixture,
$this->callMethod($oControllerMock, 'getUser')
$oUserMock,
$this->callMethod($this->_oController, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -122,6 +105,12 @@ 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'])
@ -138,23 +127,24 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(true);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
/** @var d3_totp_orderController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3TotpGetSessionObject',
'd3GetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$this->assertSame(
$this->userFixture,
$this->callMethod($oControllerMock, 'getUser')
$oUserMock,
$this->callMethod($this->_oController, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -166,6 +156,12 @@ 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'])
@ -183,22 +179,23 @@ trait d3_totp_getUserTestTrait
$oTotpMock->method('isActive')->willReturn(true);
$oTotpMock->method('loadByUserId')->willReturn(true);
/** @var d3_totp_orderController|d3_totp_UserController|d3_totp_PaymentController|MockObject $oControllerMock */
/** @var d3_totp_orderController|MockObject $oControllerMock */
$oControllerMock = $this->getMockBuilder($this->sControllerClass)
->onlyMethods([
'd3GetTotpObject',
'd3TotpGetSessionObject',
'd3GetSessionObject',
])
->getMock();
$oControllerMock->expects($this->once())->method('d3GetTotpObject')->willReturn($oTotpMock);
$oControllerMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oControllerMock->setUser($this->userFixture);
$oControllerMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$this->_oController = $oControllerMock;
$this->setValue($this->_oController, '_oActUser', $oUserMock);
$this->assertFalse(
$this->callMethod($oControllerMock, 'getUser')
$this->callMethod($this->_oController, 'getUser')
);
$oControllerMock->setUser(null);
}
/**
@ -210,32 +207,26 @@ 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($oController, 'd3GetTotpObject')
$this->callMethod($this->_oController, 'd3GetTotpObject')
);
}
/**
* @test
* @throws ReflectionException
* @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
* @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
*/
public function d3GetSessionObjectReturnsRightObject()
{
/** @var d3_totp_UserController|d3_totp_PaymentController|d3_totp_OrderController $oController */
$oController = oxNew($this->sControllerClass);
$this->assertInstanceOf(
Session::class,
$this->callMethod(
$oController,
'd3TotpGetSessionObject'
$this->_oController,
'd3GetSessionObject'
)
);
}

View File

@ -11,13 +11,9 @@
* @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;
@ -27,7 +23,25 @@ use ReflectionException;
class d3_totp_userTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @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);
}
/**
* @test
@ -40,19 +54,19 @@ class d3_totp_userTest extends d3TotpUnitTestCase
$oSessionMock = $this->getMockBuilder(Session::class)
->onlyMethods(['deleteVariable'])
->getMock();
$oSessionMock->expects($this->atLeast(2))->method('deleteVariable')->willReturn(true);
$oSessionMock->expects($this->once())->method('deleteVariable')->willReturn(true);
/** @var d3_totp_user|MockObject $oModelMock */
$oModelMock = $this->getMockBuilder(User::class)
->onlyMethods(['d3TotpGetSession'])
->onlyMethods(['d3GetSession'])
->getMock();
$oModelMock->method('d3TotpGetSession')->willReturn($oSessionMock);
$oModelMock->method('d3GetSession')->willReturn($oSessionMock);
$sut = $oModelMock;
$this->_oModel = $oModelMock;
$this->assertTrue(
$this->callMethod(
$sut,
$this->_oModel,
'logout'
)
);
@ -65,90 +79,22 @@ class d3_totp_userTest extends d3TotpUnitTestCase
*/
public function d3getTotpReturnsRightInstance()
{
$sut = oxNew(User::class);
$this->assertInstanceOf(
d3totp::class,
$this->callMethod(
$sut,
'd3getTotp'
)
$this->callMethod($this->_oModel, 'd3getTotp')
);
}
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Application\Model\d3_totp_user::d3TotpGetSession
* @covers \D3\Totp\Modules\Application\Model\d3_totp_user::d3GetSession
*/
public function d3GetSessionReturnsRightInstance()
{
$sut = oxNew(User::class);
$this->assertInstanceOf(
Session::class,
$this->callMethod(
$sut,
'd3TotpGetSession'
)
$this->callMethod($this->_oModel, 'd3GetSession')
);
}
/**
* @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,13 +11,9 @@
* @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;
@ -29,8 +25,6 @@ use ReflectionException;
class d3_totp_utilsTest extends d3TotpUnitTestCase
{
use CanAccessRestricted;
/** @var d3_totp_utils */
protected $_oCoreClass;
@ -58,7 +52,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsNoAuth()
{
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, false);
Registry::getSession()->setVariable("auth", false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -91,7 +85,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsForce2FA()
{
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, false);
Registry::getSession()->setVariable("auth", false);
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -128,7 +122,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpNotActive()
{
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
Registry::getSession()->setVariable("auth", 'foo');
/** @var d3totp|MockObject $oTotpMock */
$oTotpMock = $this->getMockBuilder(d3totp::class)
@ -165,7 +159,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpFinished()
{
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
Registry::getSession()->setVariable("auth", 'foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
@ -188,13 +182,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
$oCoreMock = $this->getMockBuilder(Utils::class)
->onlyMethods([
'd3GetTotpObject',
'd3TotpGetSessionObject',
'd3GetSessionObject',
'fetchRightsForUser',
'redirect',
])
->getMock();
$oCoreMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oCoreMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('fetchRightsForUser')->willReturn('malladmin');
$oCoreMock->expects($this->never())->method('redirect')->willReturn(true);
@ -212,7 +206,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
*/
public function checkAccessRightsTotpUnfinished()
{
Registry::getSession()->setVariable(d3totp_conf::OXID_ADMIN_AUTH, 'foo');
Registry::getSession()->setVariable("auth", 'foo');
/** @var Session|MockObject $oSessionMock */
$oSessionMock = $this->getMockBuilder(Session::class)
@ -241,13 +235,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
$oCoreMock = $this->getMockBuilder(Utils::class)
->onlyMethods([
'd3GetTotpObject',
'd3TotpGetSessionObject',
'd3GetSessionObject',
'fetchRightsForUser',
'redirect',
])
->getMock();
$oCoreMock->method('d3GetTotpObject')->willReturn($oTotpMock);
$oCoreMock->method('d3TotpGetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('d3GetSessionObject')->willReturn($oSessionMock);
$oCoreMock->method('fetchRightsForUser')->willReturn('malladmin');
$oCoreMock->expects($this->once())->method('redirect')->willReturn(true);
@ -259,13 +253,13 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase
/**
* @test
* @throws ReflectionException
* @covers \D3\Totp\Modules\Core\d3_totp_utils::d3TotpGetSessionObject
* @covers \D3\Totp\Modules\Core\d3_totp_utils::d3GetSessionObject
*/
public function d3GetSessionObjectReturnsRightInstance()
{
$this->assertInstanceOf(
Session::class,
$this->callMethod($this->_oCoreClass, 'd3TotpGetSessionObject')
$this->callMethod($this->_oCoreClass, 'd3GetSessionObject')
);
}

View File

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