improve code style

This commit is contained in:
Daniel Seifert 2024-09-21 23:53:58 +02:00
parent ceaa9daf90
commit 30e13cb0dc
Signed by: DanielS
GPG Key ID: 6A513E13AEE66170
28 changed files with 408 additions and 198 deletions

View File

@ -1,13 +1,42 @@
<?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);
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
;
$fileHeaderComment = <<<EOF
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
EOF;
$config = new PhpCsFixer\Config();
return $config->setRules([
'@PHP73Migration' => true,
'@PSR12' => true
])
'header_comment' => [
'header' => $fileHeaderComment,
'comment_type' => 'PHPDoc',
'location' => 'after_open'
],
'@PHP80Migration' => true,
'@PSR12' => true
])
->setFinder($finder)
;
;

View File

@ -1,5 +1,16 @@
<?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;
@ -27,7 +38,12 @@ class d3force_2fa extends d3user_totp
return parent::render();
}
/**
* @return bool
* @throws ContainerExceptionInterface
* @throws ModuleSettingNotFountException
* @throws NotFoundExceptionInterface
*/
protected function authorize(): bool
{
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);

View File

@ -22,13 +22,16 @@ 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 Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception as DBALException;
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\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
class d3totpadminlogin extends AdminController
@ -44,16 +47,19 @@ class d3totpadminlogin extends AdminController
}
/**
* @return d3totp|mixed
* @return d3totp
*/
public function d3TotpGetTotpObject()
public function d3TotpGetTotpObject(): d3totp
{
return oxNew(d3totp::class);
}
/**
* @return bool
* @throws DatabaseConnectionException
* @throws Exception
* @throws DBALException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
protected function isTotpIsNotRequired(): bool
{
@ -79,7 +85,10 @@ class d3totpadminlogin extends AdminController
/**
* @return string
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws DBALException
*/
public function render(): string
{
@ -109,7 +118,10 @@ class d3totpadminlogin extends AdminController
/**
* @return string|void
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws DBALException
*/
public function getBackupCodeCountMessage()
{
@ -140,16 +152,21 @@ class d3totpadminlogin extends AdminController
}
/**
* @return User
* @return d3_totp_user
*/
public function d3TotpGetUserObject(): User
{
return oxNew(User::class);
/** @var d3_totp_user $user */
$user = oxNew(User::class);
return $user;
}
/**
* @return string|void
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function checklogin()
{
@ -192,7 +209,10 @@ class d3totpadminlogin extends AdminController
* @param string|null $sTotp
* @param d3totp $totp
* @return bool
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws DBALException
* @throws d3totp_wrongOtpException
*/
public function d3TotpHasValidTotp(string $sTotp = null, d3totp $totp): bool

View File

@ -25,7 +25,6 @@ use Doctrine\DBAL\Driver\Exception as DBALDriverException;
use Exception;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\UtilsView;
use Psr\Container\ContainerExceptionInterface;
@ -122,7 +121,8 @@ class d3user_totp extends AdminDetailsController
}
/**
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function delete(): void
{

View File

@ -17,7 +17,9 @@ namespace D3\Totp\Application\Controller;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use Doctrine\DBAL\Driver\Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
trait OtpManagementControllerTrait
{
@ -47,7 +49,10 @@ trait OtpManagementControllerTrait
/**
* @return int
* @throws DatabaseConnectionException
* @throws Exception
* @throws \Doctrine\DBAL\Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getAvailableBackupCodeCount(): int
{
@ -62,4 +67,4 @@ trait OtpManagementControllerTrait
{
return oxNew(d3totp::class);
}
}
}

View File

@ -23,7 +23,6 @@ use D3\Totp\Modules\Application\Model\d3_totp_user;
use Exception;
use OxidEsales\Eshop\Application\Controller\AccountController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\UtilsView;
use Psr\Container\ContainerExceptionInterface;
@ -51,14 +50,18 @@ class d3_account_totp extends AccountController
return $this->getUser()->getId();
}
/**
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \Doctrine\DBAL\Driver\Exception
*/
public function create(): void
{
if (Registry::getRequest()->getRequestEscapedParameter('totp_use') === '1') {
try {
/** @var d3_totp_user $oUser */
$oUser = $this->getUser();
/** @var d3totp $oTotp */
$oTotp = $this->getTotpObject();
Assert::that($oTotp->checkIfAlreadyExist($this->getCurrentUserId()))->false('D3_TOTP_ALREADY_EXIST');
@ -94,11 +97,10 @@ class d3_account_totp extends AccountController
}
/**
* @throws DatabaseConnectionException
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \Doctrine\DBAL\Driver\Exception
* @throws \Doctrine\DBAL\Exception
*/
public function delete(): void
{

View File

@ -18,16 +18,18 @@ namespace D3\Totp\Application\Controller;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3backupcodelist;
use D3\Totp\Application\Model\d3totp_conf;
use Doctrine\DBAL\Driver\Exception;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Utils;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3totplogin extends FrontendController
{
protected $_sThisTemplate = '@'.Constants::OXID_MODULE_ID.'/tpl/d3totplogin';
public function render()
public function render(): string
{
if (!Registry::getSession()->hasVariable(d3totp_conf::SESSION_CURRENTUSER)) {
$this->getUtils()->redirect('index.php?cl=start', false);
@ -48,7 +50,10 @@ class d3totplogin extends FrontendController
/**
* @return string|void
* @throws DatabaseConnectionException
* @throws Exception
* @throws \Doctrine\DBAL\Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getBackupCodeCountMessage()
{
@ -72,7 +77,7 @@ class d3totplogin extends FrontendController
return oxNew(d3backupcodelist::class);
}
public function getPreviousClass()
public function getPreviousClass(): string
{
return Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTCLASS);
}
@ -101,7 +106,7 @@ class d3totplogin extends FrontendController
*
* @return array
*/
public function getBreadCrumb()
public function getBreadCrumb(): array
{
$aPaths = [];
$aPath = [];

View File

@ -1,5 +1,16 @@
<?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\Factory;
@ -12,14 +23,15 @@ use BaconQrCode\Renderer\RendererStyle\RendererStyle; // v2.0.0
class BaconQrCodeFactory
{
/**
* @param int $size
* @return RendererInterface
*/
public static function renderer($size)
public static function renderer(int $size): RendererInterface
{
return self::v200($size);
}
private static function v200($size)
private static function v200(int $size): RendererInterface
{
return oxNew(
ImageRenderer::class,

View File

@ -1,10 +1,8 @@
<?php
/**
* Copyright (c) D3 Data Development (Inh. Thomas Dartsch)
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*

View File

@ -24,7 +24,7 @@ class d3RandomGenerator extends Rand
/**
* @return string
*/
public static function getRandomTotpBackupCode()
public static function getRandomTotpBackupCode(): string
{
return self::getString(6, self::CHAR_DIGITS);
}

View File

@ -16,56 +16,65 @@ declare(strict_types=1);
namespace D3\Totp\Application\Model;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Query\QueryBuilder;
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\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3backupcode extends BaseModel
{
protected $_sCoreTable = 'd3totp_backupcodes';
/**
* @param $sUserId
* @param string $sUserId
* @return string
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws \Doctrine\DBAL\Exception
*/
public function generateCode($sUserId)
public function generateCode(string $sUserId): string
{
$sCode = $this->getRandomTotpBackupCode();
$this->assign(
[
'oxuserid' => $sUserId,
'backupcode' => $this->d3EncodeBC($sCode, $sUserId),
]
);
$this->assign([
'oxuserid' => $sUserId,
'backupcode' => $this->d3EncodeBC($sCode, $sUserId),
]);
return $sCode;
}
public function getRandomTotpBackupCode()
public function getRandomTotpBackupCode(): string
{
return d3RandomGenerator::getRandomTotpBackupCode();
}
/**
* @param $code
* @param $sUserId
* @return false|string
* @throws DatabaseConnectionException
* @param string $code
* @param string $sUserId
* @return string
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws \Doctrine\DBAL\Exception
*/
public function d3EncodeBC($code, $sUserId)
public function d3EncodeBC(string $code, string $sUserId): string
{
$oDb = DatabaseProvider::getDb();
$oUser = $this->d3TotpGetUserObject();
$oUser->load($sUserId);
$salt = $oUser->getFieldData('oxpasssalt');
$sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )";
return $oDb->getOne($sSelect);
$qb = $this->getQueryBuilder();
$qb->select('BINARY MD5( CONCAT('.$qb->createNamedParameter($code).', UNHEX('.$qb->createNamedParameter($salt).')))');
return $qb->execute()->fetchOne();
}
public function d3GetUser()
public function d3GetUser(): User
{
/** @var User|null $user */
$user = $this->getUser();
@ -84,8 +93,18 @@ class d3backupcode extends BaseModel
/**
* @return User
*/
public function d3TotpGetUserObject()
public function d3TotpGetUserObject(): User
{
return oxNew(User::class);
}
/**
* @return QueryBuilder
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getQueryBuilder(): QueryBuilder
{
return ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
}
}

View File

@ -16,32 +16,36 @@ declare(strict_types=1);
namespace D3\Totp\Application\Model;
use D3\Totp\Application\Controller\Admin\d3user_totp;
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Query\QueryBuilder;
use Exception;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\ListModel;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3backupcodelist extends ListModel
{
protected $_sObjectsInListName = d3backupcode::class;
/**
* Core table name
*
* @var string
*/
/** @var string */
protected $_sCoreTable = 'd3totp_backupcodes';
protected $_backupCodes = [];
protected array $_backupCodes = [];
/**
* @param $sUserId
* @throws DatabaseConnectionException
* @param string $sUserId
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws DBALDriverException
* @throws DBALException
*/
public function generateBackupCodes($sUserId)
public function generateBackupCodes(string $sUserId): void
{
$this->deleteAllFromUser($sUserId);
@ -59,7 +63,7 @@ class d3backupcodelist extends ListModel
/**
* @return d3backupcode
*/
public function getD3BackupCodeObject()
public function getD3BackupCodeObject(): d3backupcode
{
return oxNew(d3backupcode::class);
}
@ -67,7 +71,7 @@ class d3backupcodelist extends ListModel
/**
* @return Config
*/
public function d3GetConfig()
public function d3GetConfig(): Config
{
return Registry::getConfig();
}
@ -75,7 +79,7 @@ class d3backupcodelist extends ListModel
/**
* @throws Exception
*/
public function save()
public function save(): void
{
/** @var d3backupcode $oBackupCode */
foreach ($this->getArray() as $oBackupCode) {
@ -86,7 +90,7 @@ class d3backupcodelist extends ListModel
/**
* @return d3backupcode
*/
public function getBaseObject()
public function getBaseObject(): d3backupcode
{
/** @var d3backupcode $object */
$object = parent::getBaseObject();
@ -95,19 +99,32 @@ class d3backupcodelist extends ListModel
}
/**
* @param $totp
* @param string $totp
* @return bool
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws DBALDriverException
* @throws DBALException
*/
public function verify($totp)
public function verify(string $totp): bool
{
$oDb = $this->d3GetDb();
$qb = $this->getQueryBuilder();
$qb->select('oxid')
->from($this->getBaseObject()->getViewName())
->where(
$qb->expr()->and(
$qb->expr()->eq(
'backupcode',
$qb->createNamedParameter($this->getBaseObject()->d3EncodeBC($totp, $this->d3GetUser()->getId()))
),
$qb->expr()->eq(
'oxuserid',
$qb->createNamedParameter($this->d3GetUser()->getId())
)
)
);
$query = "SELECT oxid FROM ".$this->getBaseObject()->getViewName().
" WHERE ".$oDb->quoteIdentifier('backupcode')." = ".$oDb->quote($this->getBaseObject()->d3EncodeBC($totp, $this->d3GetUser()->getId()))." AND ".
$oDb->quoteIdentifier("oxuserid") ." = ".$oDb->quote($this->d3GetUser()->getId());
$sVerify = $oDb->getOne($query);
$sVerify = $qb->execute()->fetchOne();
if ($sVerify) {
$this->getBaseObject()->delete($sVerify);
@ -117,26 +134,23 @@ class d3backupcodelist extends ListModel
}
/**
* @return DatabaseInterface
* @throws DatabaseConnectionException
* @param string $sUserId
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function d3GetDb()
public function deleteAllFromUser(string $sUserId): void
{
return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC);
}
$qb = $this->getQueryBuilder();
$qb->select('oxid')
->from($this->getBaseObject()->getCoreTableName())
->where(
$qb->expr()->eq(
'oxuserid',
$qb->createNamedParameter($sUserId)
)
);
/**
* @param $sUserId
* @throws DatabaseConnectionException
*/
public function deleteAllFromUser($sUserId)
{
$oDb = $this->d3GetDb();
$query = "SELECT OXID FROM ".$oDb->quoteIdentifier($this->getBaseObject()->getCoreTableName()).
" WHERE ".$oDb->quoteIdentifier('oxuserid')." = ".$oDb->quote($sUserId);
$this->selectString($query);
$this->selectString($qb->getSQL(), $qb->getParameters());
/** @var d3backupcode $oBackupCode */
foreach ($this->getArray() as $oBackupCode) {
@ -145,22 +159,40 @@ class d3backupcodelist extends ListModel
}
/**
* @param $sUserId
* @param string $sUserId
* @return int
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws DBALDriverException
* @throws DBALException
*/
public function getAvailableCodeCount($sUserId)
public function getAvailableCodeCount(string $sUserId): int
{
$oDb = $this->d3GetDb();
$qb = $this->getQueryBuilder();
$qb->select('count(*)')
->from($this->getBaseObject()->getViewName())
->where(
$qb->expr()->eq(
'oxuserid',
$qb->createNamedParameter($sUserId)
)
);
$query = "SELECT count(*) FROM ".$oDb->quoteIdentifier($this->getBaseObject()->getViewName()).
" WHERE ".$oDb->quoteIdentifier('oxuserid')." = ".$oDb->quote($sUserId);
return (int) $oDb->getOne($query);
return (int) $qb->execute()->fetchOne();
}
public function d3GetUser()
public function d3GetUser(): User
{
return $this->getBaseObject()->d3GetUser();
}
/**
* @return QueryBuilder
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getQueryBuilder(): QueryBuilder
{
return ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
}
}

View File

@ -25,7 +25,6 @@ use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Query\QueryBuilder;
use OTPHP\TOTP;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
@ -54,13 +53,13 @@ class d3totp extends BaseModel
}
/**
* @param $userId
* @param string $userId
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function loadByUserId($userId): void
public function loadByUserId(string $userId): void
{
$this->userId = $userId;
@ -69,7 +68,6 @@ class d3totp extends BaseModel
->executeQuery()
->fetchOne()
) {
/** @var QueryBuilder $qb */
$qb = $this->getQueryBuilder();
$qb->select('oxid')
->from($this->getViewName())
@ -82,14 +80,14 @@ class d3totp extends BaseModel
}
/**
* @param $userId
* @param string $userId
* @return bool
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function checkIfAlreadyExist($userId): bool
public function checkIfAlreadyExist(string $userId): bool
{
$qb = $this->getQueryBuilder();
$qb->select('1')
@ -98,6 +96,7 @@ class d3totp extends BaseModel
$qb->expr()->eq('oxuserid', $qb->createNamedParameter($userId))
)
->setMaxResults(1);
return (bool) $qb->execute()->fetchOne();
}
@ -177,10 +176,10 @@ class d3totp extends BaseModel
}
/**
* @param $seed
* @param string|null $seed
* @return TOTP
*/
public function getTotp($seed = null): TOTP
public function getTotp(string $seed = null): TOTP
{
if (null == $this->totp) {
$this->totp = TOTP::create($seed ?: $this->getSavedSecret());
@ -220,9 +219,9 @@ class d3totp extends BaseModel
}
/**
* @param $seed
* @param string $seed
*/
public function saveSecret($seed): void
public function saveSecret(string $seed): void
{
$this->assign([
'seed' => $this->encrypt($seed),
@ -230,13 +229,16 @@ class d3totp extends BaseModel
}
/**
* @param $totp
* @param $seed
* @param string $totp
* @param string|null $seed
* @return bool
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws d3totp_wrongOtpException
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function verify($totp, $seed = null): bool
public function verify(string $totp, string $seed = null): bool
{
$blNotVerified = $this->getTotp($seed)->verify($totp, null, $this->timeWindow) == false;
@ -245,10 +247,14 @@ class d3totp extends BaseModel
$blNotVerified = $oBC->verify($totp) == false;
if ($blNotVerified) {
throw oxNew(d3totp_wrongOtpException::class);
/** @var d3totp_wrongOtpException $exception */
$exception = oxNew(d3totp_wrongOtpException::class);
throw $exception;
}
} elseif ($blNotVerified && $seed !== null) {
throw oxNew(d3totp_wrongOtpException::class);
/** @var d3totp_wrongOtpException $exception */
$exception = oxNew(d3totp_wrongOtpException::class);
throw $exception;
}
return !$blNotVerified;
@ -263,12 +269,12 @@ class d3totp extends BaseModel
}
/**
* @param $plaintext
* @param string $plaintext
* @return string
*/
public function encrypt($plaintext): string
public function encrypt(string $plaintext): string
{
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, self::ENC_KEY, OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, self::ENC_KEY, true);
@ -276,16 +282,16 @@ class d3totp extends BaseModel
}
/**
* @param $ciphertext
* @param string $ciphertext
* @return false|string
*/
public function decrypt($ciphertext): false|string
public function decrypt(string $ciphertext): false|string
{
$c = $this->d3Base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$ivlen = openssl_cipher_iv_length($cipher = "AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$hmac = substr($c, $ivlen, $sha2len = 32);
$ciphertext_raw = substr($c, $ivlen + $sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, self::ENC_KEY, OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, self::ENC_KEY, true);
if (hash_equals($hmac, $calcmac)) { // PHP 5.6+ compute attack-safe comparison
@ -297,18 +303,19 @@ class d3totp extends BaseModel
/**
* required for unit tests
* @param $source
* @return bool|string
* @param string $source
* @return string
*/
public function d3Base64_decode($source): bool|string
public function d3Base64_decode(string $source): string
{
return base64_decode($source);
}
/**
* @param null|string $oxid
* @param string|null $oxid
* @return bool
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function delete($oxid = null): bool
{

View File

@ -24,7 +24,6 @@ use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception as 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;
@ -38,7 +37,10 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @param User $oUser
*
* @return string
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
protected function afterLogin($oUser)
{
@ -68,7 +70,8 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
$sUrl = Registry::getConfig()->getShopHomeUrl() . 'cl=d3totplogin';
$this->d3TotpGetUtils()->redirect($sUrl, false);
}
} catch (InvalidArgumentException) {}
} catch (InvalidArgumentException) {
}
return parent::afterLogin($oUser);
}
@ -83,10 +86,9 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
/**
* @return false|string
* @throws DatabaseConnectionException
* @throws Exception
* @throws DBALException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function d3TotpCheckTotpLogin(): false|string
@ -148,7 +150,7 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @param d3totp $totp
* @return bool
*/
public function d3TotpIsNoTotpOrNoLogin($totp): bool
public function d3TotpIsNoTotpOrNoLogin(d3totp $totp): bool
{
return false == Registry::getSession()->getVariable(d3totp_conf::SESSION_CURRENTUSER)
|| false == $totp->isActive();
@ -158,10 +160,13 @@ class d3_totp_UserComponent extends d3_totp_UserComponent_parent
* @param string $sTotp
* @param d3totp $totp
* @return bool
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws d3totp_wrongOtpException
*/
public function d3TotpHasValidTotp($sTotp, $totp): bool
public function d3TotpHasValidTotp(string $sTotp, d3totp $totp): bool
{
return Registry::getSession()->getVariable(d3totp_conf::SESSION_AUTH) ||
$totp->verify($sTotp);

View File

@ -18,9 +18,6 @@ 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 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;
@ -48,7 +45,6 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
/**
* @return mixed|string
* @throws DatabaseConnectionException
*/
public function checklogin()
{
@ -74,13 +70,13 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
$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, "/");
$myUtilsServer->setOxCookie("oxidadminprofile", $sProfile . "@" . implode("@", $aProfiles[$sProfile]), time() + 31536000);
$this->d3TotpGetSession()->setVariable("profile", $aProfiles[$sProfile]);
$this->d3TotpGetSession()->deleteVariable(d3totp_conf::SESSION_ADMIN_PROFILE);
}
} else {
//deleting cookie info, as setting profile to default
$myUtilsServer->setOxCookie("oxidadminprofile", "", time() - 3600, "/");
$myUtilsServer->setOxCookie("oxidadminprofile", "", time() - 3600);
}
$this->d3totpAfterLoginSetLanguage();
@ -97,7 +93,7 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
$iLang = key($aLanguages);
}
$myUtilsServer->setOxCookie("oxidadminlanguage", $aLanguages[$iLang]->abbr, time() + 31536000, "/");
$myUtilsServer->setOxCookie("oxidadminlanguage", $aLanguages[$iLang]->abbr, time() + 31536000);
$this->d3TotpGetLangObject()->setTplLanguage($iLang);
}

View File

@ -18,20 +18,25 @@ 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 Doctrine\DBAL\Driver\Exception;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
trait d3_totp_getUserTrait
{
use IsMockable;
/**
* @return bool|object|User
* @throws DatabaseConnectionException
* @return false|User
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws \Doctrine\DBAL\Exception
*/
public function getUser()
public function getUser(): false|User
{
/** @var User|null $user */
$user = $this->d3CallMockableFunction([$this->parentClass, 'getUser']);
@ -55,7 +60,7 @@ trait d3_totp_getUserTrait
/**
* @return d3totp
*/
public function d3GetTotpObject()
public function d3GetTotpObject(): d3totp
{
return oxNew(d3totp::class);
}
@ -63,7 +68,7 @@ trait d3_totp_getUserTrait
/**
* @return Session
*/
public function d3TotpGetSessionObject()
public function d3TotpGetSessionObject(): Session
{
return Registry::getSession();
}

View File

@ -18,9 +18,8 @@ namespace D3\Totp\Modules\Core;
use D3\Totp\Application\Model\Constants;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\d3totp_conf;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Exception;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
@ -34,8 +33,11 @@ class d3_totp_utils extends d3_totp_utils_parent
{
/**
* @return bool
* @throws DBALException
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws ModuleSettingNotFountException
* @throws NotFoundExceptionInterface
* @throws Exception
* @throws \Doctrine\DBAL\Exception
*/
public function checkAccessRights()
{
@ -43,7 +45,6 @@ class d3_totp_utils extends d3_totp_utils_parent
$blAuth = $this->d3AuthHook($blAuth);
$userID = $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
$totpAuth = (bool) $this->d3TotpGetSessionObject()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);
/** @var d3totp $totp */
$totp = $this->d3GetTotpObject();
$totp->loadByUserId($userID);

View File

@ -19,15 +19,26 @@ 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 Doctrine\DBAL\Driver\Exception as DBALDriverException;
use Doctrine\DBAL\Exception as DBALException;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\Utils;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class totpSystemEventHandler extends totpSystemEventHandler_parent
{
use IsMockable;
/**
* @return void
* @throws ContainerExceptionInterface
* @throws DBALDriverException
* @throws DBALException
* @throws NotFoundExceptionInterface
*/
public function onAdminLogin()
{
$this->d3RequestTotp();
@ -35,7 +46,14 @@ class totpSystemEventHandler extends totpSystemEventHandler_parent
$this->d3CallMockableFunction([totpSystemEventHandler_parent::class, 'onAdminLogin']);
}
protected function d3requestTotp()
/**
* @return void
* @throws DBALDriverException
* @throws DBALException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
protected function d3requestTotp(): void
{
$totp = $this->d3GetTotpObject();
$userId = $this->d3TotpGetSession()->getVariable(d3totp_conf::OXID_ADMIN_AUTH);
@ -86,7 +104,7 @@ class totpSystemEventHandler extends totpSystemEventHandler_parent
* @param d3totp $totp
* @return bool
*/
public function d3TotpLoginMissing($totp)
public function d3TotpLoginMissing(d3totp $totp)
{
return $totp->isActive()
&& false == $this->d3TotpGetSession()->getVariable(d3totp_conf::SESSION_ADMIN_AUTH);

View File

@ -3,7 +3,7 @@
# 2-factor authentication (one-time password) 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 / TOTP) for login in front- and backend in addition to username and password.
## Features
@ -48,7 +48,7 @@ The necessary configuration can be found in the same area in the "Settings" tab.
## Changelog
See [CHANGELOG](CHANGELOG.md) for further informations.
See [CHANGELOG](CHANGELOG.md) for further information.
## Contributing

View File

@ -32,13 +32,13 @@ use Psr\Container\NotFoundExceptionInterface;
class Actions
{
public array $seo_de = [
'2-faktor-authentisierung/'
'2-faktor-authentisierung/',
];
public array $seo_en = [
'en/2-factor-authentication/'
'en/2-factor-authentication/',
];
public array $stdClassName = [
'd3_account_totp'
'd3_account_totp',
];
/**
@ -112,7 +112,7 @@ class Actions
return true;
}
protected function hasSeoUrl($item, $langId): bool
protected function hasSeoUrl(string $item, int $langId): bool
{
$seoEncoder = oxNew(SeoEncoder::class);
$seoUrl = $seoEncoder->getStaticUrl(

View File

@ -15,16 +15,14 @@ declare(strict_types=1);
namespace D3\Totp\Setup;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use Exception;
// @codeCoverageIgnoreStart
class Events
{
/**
* @return void
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws Exception
*/
public static function onActivate(): void
{
@ -38,7 +36,7 @@ class Events
/**
* @codeCoverageIgnore
*/
public static function onDeactivate()
public static function onDeactivate(): void
{
}
}

View File

@ -1,5 +1,16 @@
<?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
*/
namespace D3\Totp\tests\unit\Application\Controller\Admin;
use D3\Totp\Application\Controller\Admin\d3force_2fa;

View File

@ -1,5 +1,16 @@
<?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
*/
namespace D3\Totp\tests\unit\Application\Factory;
use BaconQrCode\Renderer\ImageRenderer;

View File

@ -271,7 +271,7 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase
public function d3TotpLoginMissingTestDataProvider(): array
{
return [
'totp not active, not logged in'=> [false, false, false],
'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],

View File

@ -1,4 +1,5 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.

View File

@ -49,6 +49,7 @@
"beberlei/assert": "^v3.3.2"
},
"require-dev": {
"phpunit/phpunit" : "^10.5",
"friendsofphp/php-cs-fixer": "^3.9",
"phpstan/phpstan": "^1.8",
"boxblinkracer/phpunuhi": "^1.12"

View File

@ -1,5 +1,16 @@
<?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\Migrations;
@ -14,7 +25,7 @@ use Doctrine\Migrations\AbstractMigration;
final class Version20240905232017 extends AbstractMigration
{
public function getDescription() : string
public function getDescription(): string
{
return 'Extend Database by missing OTP tables and missing columns.';
}
@ -22,7 +33,7 @@ final class Version20240905232017 extends AbstractMigration
/**
* @throws Exception
*/
public function up(Schema $schema) : void
public function up(Schema $schema): void
{
$this->connection->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
@ -79,9 +90,9 @@ final class Version20240905232017 extends AbstractMigration
->setDefault('CURRENT_TIMESTAMP');
}
$table->hasPrimaryKey() ?:$table->setPrimaryKey(['oxid']);
$table->hasPrimaryKey() ?: $table->setPrimaryKey(['oxid']);
if($table->hasIndex('OXUSERID') === false){
if ($table->hasIndex('OXUSERID') === false) {
$table->addUniqueIndex(['OXUSERID'], 'OXUSERID');
}
}
@ -129,18 +140,18 @@ final class Version20240905232017 extends AbstractMigration
->setDefault('CURRENT_TIMESTAMP');
}
$table->hasPrimaryKey() ?:$table->setPrimaryKey(['oxid']);
$table->hasPrimaryKey() ?: $table->setPrimaryKey(['oxid']);
if($table->hasIndex('OXUSERID') === false){
if ($table->hasIndex('OXUSERID') === false) {
$table->addIndex(['OXUSERID'], 'OXUSERID');
}
if($table->hasIndex('BACKUPCODE') === false){
if ($table->hasIndex('BACKUPCODE') === false) {
$table->addIndex(['BACKUPCODE'], 'BACKUPCODE');
}
}
public function down(Schema $schema) : void
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
}

View File

@ -1,14 +1,21 @@
parameters:
scanFiles:
- src/IntelliSenseHelper.php
- IntelliSenseHelper.php
- ../../oxid-esales/oxideshop-ce/source/bootstrap.php
- ../../oxid-esales/oxideshop-ce/source/oxfunctions.php
- ../../oxid-esales/oxideshop-ce/source/overridablefunctions.php
paths:
- src
level: 5
phpVersion: 70300
- .
excludePaths:
- .php-cs-fixer.php
- rector.php
- Tests
level: 6
phpVersion: 80300
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.#'
- '#Return type [()a-z]* of method .*getBreadCrumb\(\) should be compatible with return type [()a-z]* of method\s*.*getBreadCrumb\(\)#'
- '#Unable to resolve the template type T in call to function oxNew#'
- '#(Property|Method) D3\\Totp\\Modules\\.* has no (return type|type) specified.#'
- identifier: missingType.iterableValue
parallel:
processTimeout: 900.0