add further tests
This commit is contained in:
parent
e80182f5e4
commit
9f0ad7d26f
@ -25,7 +25,7 @@ use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
|
|||||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||||
use Doctrine\DBAL\Exception;
|
use Doctrine\DBAL\Exception;
|
||||||
use OxidEsales\Eshop\Application\Model\User;
|
use OxidEsales\Eshop\Application\Model\User;
|
||||||
use OxidEsales\Eshop\Core\Registry;
|
use OxidEsales\Eshop\Core\Config;
|
||||||
use OxidEsales\Eshop\Core\Request;
|
use OxidEsales\Eshop\Core\Request;
|
||||||
use OxidEsales\Eshop\Core\Session;
|
use OxidEsales\Eshop\Core\Session;
|
||||||
use OxidEsales\Eshop\Core\Utils;
|
use OxidEsales\Eshop\Core\Utils;
|
||||||
@ -46,41 +46,79 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
|
|||||||
*/
|
*/
|
||||||
public function login()
|
public function login()
|
||||||
{
|
{
|
||||||
$lgn_user = $this->d3GetMockableRegistryObject(Request::class)->getRequestParameter('lgn_usr');
|
$this->d3WebauthnLogin();
|
||||||
$password = $this->d3GetMockableRegistryObject(Request::class)->getRequestParameter('lgn_pwd');
|
|
||||||
|
return $this->d3CallMockableFunction([d3_webauthn_UserComponent_parent::class, 'login']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws DoctrineDriverException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function d3WebauthnLogin(): void
|
||||||
|
{
|
||||||
|
$lgn_user = $this->d3GetMockableRegistryObject(Request::class)->getRequestParameter( 'lgn_usr');
|
||||||
/** @var d3_User_Webauthn $user */
|
/** @var d3_User_Webauthn $user */
|
||||||
$user = oxNew(User::class);
|
$user = $this->d3GetMockableOxNewObject(User::class);
|
||||||
$userId = $user->d3GetLoginUserId($lgn_user);
|
$userId = $user->d3GetLoginUserId($lgn_user);
|
||||||
|
|
||||||
if ($lgn_user && $userId && !strlen(trim((string) $password))) {
|
if ( $this->d3CanUseWebauthn( $lgn_user, $userId)) {
|
||||||
$webauthn = $this->d3GetMockableOxNewObject(Webauthn::class);
|
if ($this->d3HasWebauthnButNotLoggedin($userId)) {
|
||||||
|
$session = $this->d3GetMockableRegistryObject(Session::class);
|
||||||
if ($webauthn->isActive($userId)
|
$session->setVariable(
|
||||||
&& !Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
|
|
||||||
) {
|
|
||||||
Registry::getSession()->setVariable(
|
|
||||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
|
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
|
||||||
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
|
$this->getClassKey() != 'd3webauthnlogin' ? $this->getClassKey() : 'start'
|
||||||
Registry::getSession()->setVariable(
|
);
|
||||||
|
$session->setVariable(
|
||||||
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
|
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
|
||||||
$userId
|
$userId
|
||||||
);
|
);
|
||||||
|
$session->setVariable(
|
||||||
Registry::getSession()->setVariable(
|
|
||||||
WebauthnConf::WEBAUTHN_SESSION_NAVPARAMS,
|
WebauthnConf::WEBAUTHN_SESSION_NAVPARAMS,
|
||||||
$this->getParent()->getNavigationParams()
|
$this->getParent()->getNavigationParams()
|
||||||
);
|
);
|
||||||
Registry::getSession()->setVariable(
|
$session->setVariable(
|
||||||
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
|
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
|
||||||
$this->getParent()->getViewConfig()->getNavFormParams()
|
$this->getParent()->getViewConfig()->getNavFormParams()
|
||||||
);
|
);
|
||||||
|
|
||||||
$sUrl = Registry::getConfig()->getShopHomeUrl() . 'cl=d3webauthnlogin';
|
$sUrl = $this->d3GetMockableRegistryObject(Config::class)->getShopHomeUrl() . 'cl=d3webauthnlogin';
|
||||||
$this->d3GetMockableRegistryObject(Utils::class)->redirect($sUrl);
|
$this->d3GetMockableRegistryObject(Utils::class)->redirect($sUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return parent::login();
|
/**
|
||||||
|
* @param $lgn_user
|
||||||
|
* @param string|null $userId
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function d3CanUseWebauthn( $lgn_user, ?string $userId): bool
|
||||||
|
{
|
||||||
|
$password = $this->d3GetMockableRegistryObject(Request::class)->getRequestParameter( 'lgn_pwd');
|
||||||
|
|
||||||
|
return $lgn_user &&
|
||||||
|
$userId &&
|
||||||
|
false === $this->d3GetMockableRegistryObject(Session::class)
|
||||||
|
->hasVariable( WebauthnConf::WEBAUTHN_SESSION_AUTH ) &&
|
||||||
|
( ! strlen( trim( (string) $password ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $userId
|
||||||
|
* @return bool
|
||||||
|
* @throws DoctrineDriverException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function d3HasWebauthnButNotLoggedin($userId): bool
|
||||||
|
{
|
||||||
|
$webauthn = $this->d3GetMockableOxNewObject(Webauthn::class);
|
||||||
|
|
||||||
|
return $webauthn->isActive($userId)
|
||||||
|
&& !$this->d3GetMockableRegistryObject(Session::class)
|
||||||
|
->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,8 +20,7 @@ use D3\Webauthn\Application\Model\WebauthnConf;
|
|||||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||||
use Doctrine\DBAL\Exception;
|
use Doctrine\DBAL\Exception;
|
||||||
use Doctrine\DBAL\Query\QueryBuilder;
|
use Doctrine\DBAL\Query\QueryBuilder;
|
||||||
use OxidEsales\Eshop\Core\Exception\UserException;
|
use OxidEsales\Eshop\Core\Config;
|
||||||
use OxidEsales\Eshop\Core\Registry;
|
|
||||||
use OxidEsales\Eshop\Core\Session;
|
use OxidEsales\Eshop\Core\Session;
|
||||||
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
|
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
|
||||||
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
|
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
|
||||||
@ -37,7 +36,16 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
|
|||||||
public function logout()
|
public function logout()
|
||||||
{
|
{
|
||||||
$return = $this->d3CallMockableFunction([d3_User_Webauthn_parent::class, 'logout']);
|
$return = $this->d3CallMockableFunction([d3_User_Webauthn_parent::class, 'logout']);
|
||||||
|
$this->d3WebauthnLogout();
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function d3WebauthnLogout(): void
|
||||||
|
{
|
||||||
$session = $this->d3GetMockableRegistryObject(Session::class);
|
$session = $this->d3GetMockableRegistryObject(Session::class);
|
||||||
$session->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH);
|
$session->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH);
|
||||||
$session->deleteVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT);
|
$session->deleteVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT);
|
||||||
@ -52,26 +60,36 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
|
|||||||
$session->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTCLASS);
|
$session->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTCLASS);
|
||||||
|
|
||||||
$session->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS);
|
$session->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS);
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $userName
|
* @param $userName
|
||||||
* @param $password
|
* @param $password
|
||||||
* @param $setSessionCookie
|
* @param bool $setSessionCookie
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws UserException
|
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function login($userName, $password, $setSessionCookie = false)
|
public function login($userName, $password, $setSessionCookie = false)
|
||||||
|
{
|
||||||
|
$userName = $this->d3WebauthnLogin($userName);
|
||||||
|
|
||||||
|
return $this->d3CallMockableFunction([d3_User_Webauthn_parent::class, 'login'], [$userName, $password, $setSessionCookie]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userName
|
||||||
|
* @return mixed|string|null
|
||||||
|
* @throws ReflectionException
|
||||||
|
*/
|
||||||
|
protected function d3WebauthnLogin(string $userName)
|
||||||
{
|
{
|
||||||
$session = $this->d3GetMockableRegistryObject(Session::class);
|
$session = $this->d3GetMockableRegistryObject(Session::class);
|
||||||
|
|
||||||
if ($session->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)) {
|
if ($session->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) &&
|
||||||
|
$userName === $session->getVariable(WebauthnConf::WEBAUTHN_SESSION_LOGINUSER)
|
||||||
|
) {
|
||||||
$userName = $userName ?: $session->getVariable(WebauthnConf::WEBAUTHN_SESSION_LOGINUSER);
|
$userName = $userName ?: $session->getVariable(WebauthnConf::WEBAUTHN_SESSION_LOGINUSER);
|
||||||
$config = Registry::getConfig();
|
$shopId = $this->d3GetMockableRegistryObject(Config::class)->getShopId();
|
||||||
$shopId = $config->getShopId();
|
|
||||||
|
|
||||||
/** private method is out of scope */
|
/** private method is out of scope */
|
||||||
$class = new ReflectionClass($this);
|
$class = new ReflectionClass($this);
|
||||||
@ -80,17 +98,16 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
|
|||||||
$method->invokeArgs(
|
$method->invokeArgs(
|
||||||
$this,
|
$this,
|
||||||
[
|
[
|
||||||
$session->getVariable(WebauthnConf::WEBAUTHN_SESSION_LOGINUSER),
|
$userName,
|
||||||
$shopId
|
$shopId
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return $userName;
|
||||||
return parent::login($userName, $password, $setSessionCookie);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $username
|
* @param string|null $username
|
||||||
* @param string|null $rights
|
* @param string|null $rights
|
||||||
* @return string|null
|
* @return string|null
|
||||||
* @throws ContainerExceptionInterface
|
* @throws ContainerExceptionInterface
|
||||||
@ -98,7 +115,7 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @throws NotFoundExceptionInterface
|
* @throws NotFoundExceptionInterface
|
||||||
*/
|
*/
|
||||||
public function d3GetLoginUserId(string $username, string $rights = null): ?string
|
public function d3GetLoginUserId(?string $username, string $rights = null): ?string
|
||||||
{
|
{
|
||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
return null;
|
return null;
|
||||||
@ -116,9 +133,10 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
|
|||||||
),
|
),
|
||||||
$qb->expr()->eq(
|
$qb->expr()->eq(
|
||||||
'oxshopid',
|
'oxshopid',
|
||||||
$qb->createNamedParameter(Registry::getConfig()->getShopId())
|
$qb->createNamedParameter($this->d3GetMockableRegistryObject(Config::class)->getShopId())
|
||||||
),
|
),
|
||||||
$rights ? $qb->expr()->eq(
|
$rights ?
|
||||||
|
$qb->expr()->eq(
|
||||||
'oxrights',
|
'oxrights',
|
||||||
$qb->createNamedParameter($rights)
|
$qb->createNamedParameter($rights)
|
||||||
) : '1'
|
) : '1'
|
||||||
|
195
src/Setup/Actions.php
Normal file
195
src/Setup/Actions.php
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<?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\Webauthn\Setup;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
||||||
|
use Doctrine\DBAL\Exception as DoctrineException;
|
||||||
|
use Doctrine\DBAL\Query\QueryBuilder;
|
||||||
|
use Exception;
|
||||||
|
use OxidEsales\Eshop\Core\DatabaseProvider;
|
||||||
|
use OxidEsales\Eshop\Core\DbMetaDataHandler;
|
||||||
|
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||||
|
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
|
||||||
|
use OxidEsales\Eshop\Core\Registry;
|
||||||
|
use OxidEsales\Eshop\Core\UtilsView;
|
||||||
|
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
|
||||||
|
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
|
||||||
|
class Actions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* SQL statement, that will be executed only at the first time of module installation.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $createCredentialSql =
|
||||||
|
"CREATE TABLE `d3wa_usercredentials` (
|
||||||
|
`OXID` char(32) NOT NULL,
|
||||||
|
`OXUSERID` char(32) NOT NULL,
|
||||||
|
`OXSHOPID` int(11) NOT NULL,
|
||||||
|
`NAME` varchar(100) NOT NULL,
|
||||||
|
`CREDENTIALID` char(128) NOT NULL,
|
||||||
|
`CREDENTIAL` varchar(2000) NOT NULL,
|
||||||
|
`OXTIMESTAMP` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||||
|
PRIMARY KEY (`OXID`),
|
||||||
|
KEY `CREDENTIALID_IDX` (`CREDENTIALID`),
|
||||||
|
KEY `SHOPUSER_IDX` (`OXUSERID`,`OXSHOPID`) USING BTREE
|
||||||
|
) ENGINE=InnoDB COMMENT='WebAuthn Credentials';";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the sql at the first time of the module installation.
|
||||||
|
* @return void
|
||||||
|
* @throws DatabaseConnectionException
|
||||||
|
* @throws DatabaseErrorException
|
||||||
|
*/
|
||||||
|
public function setupModule()
|
||||||
|
{
|
||||||
|
if (!$this->tableExists('d3wa_usercredentials')) {
|
||||||
|
$this->executeSQL($this->createCredentialSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if table exists
|
||||||
|
*
|
||||||
|
* @param string $sTableName table name
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function tableExists(string $sTableName): bool
|
||||||
|
{
|
||||||
|
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
||||||
|
|
||||||
|
return $oDbMetaDataHandler->tableExists($sTableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes given sql statement.
|
||||||
|
*
|
||||||
|
* @param string $sSQL Sql to execute.
|
||||||
|
* @throws DatabaseConnectionException
|
||||||
|
* @throws DatabaseErrorException
|
||||||
|
*/
|
||||||
|
public function executeSQL(string $sSQL)
|
||||||
|
{
|
||||||
|
DatabaseProvider::getDb()->execute($sSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if field exists in table
|
||||||
|
*
|
||||||
|
* @param string $sFieldName field name
|
||||||
|
* @param string $sTableName table name
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function fieldExists(string $sFieldName, string $sTableName): bool
|
||||||
|
{
|
||||||
|
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
||||||
|
|
||||||
|
return $oDbMetaDataHandler->fieldExists($sFieldName, $sTableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate views for changed tables
|
||||||
|
*/
|
||||||
|
public function regenerateViews()
|
||||||
|
{
|
||||||
|
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
||||||
|
$oDbMetaDataHandler->updateViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear cache
|
||||||
|
*/
|
||||||
|
public function clearCache()
|
||||||
|
{
|
||||||
|
/** @var UtilsView $oUtilsView */
|
||||||
|
$oUtilsView = Registry::getUtilsView();
|
||||||
|
$sSmartyDir = $oUtilsView->getSmartyDir();
|
||||||
|
|
||||||
|
if ($sSmartyDir && is_readable($sSmartyDir)) {
|
||||||
|
foreach (glob($sSmartyDir . '*') as $sFile) {
|
||||||
|
if (!is_dir($sFile)) {
|
||||||
|
@unlink($sFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function seoUrl()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!self::hasSeoUrl()) {
|
||||||
|
self::createSeoUrl();
|
||||||
|
}
|
||||||
|
} catch (Exception|NotFoundExceptionInterface|DoctrineDriverException|ContainerExceptionInterface $e) {
|
||||||
|
Registry::getUtilsView()->addErrorToDisplay('error wile creating SEO URLs: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @throws DoctrineDriverException
|
||||||
|
* @throws DoctrineException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
public function hasSeoUrl(): bool
|
||||||
|
{
|
||||||
|
/** @var QueryBuilder $qb */
|
||||||
|
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
|
||||||
|
$qb->select('1')
|
||||||
|
->from('oxseo')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->and(
|
||||||
|
$qb->expr()->eq(
|
||||||
|
'oxstdurl',
|
||||||
|
$qb->createNamedParameter('index.php?cl=d3_account_webauthn')
|
||||||
|
),
|
||||||
|
$qb->expr()->eq(
|
||||||
|
'oxshopid',
|
||||||
|
$qb->createNamedParameter(Registry::getConfig()->getShopId())
|
||||||
|
),
|
||||||
|
$qb->expr()->eq(
|
||||||
|
'oxlang',
|
||||||
|
$qb->createNamedParameter('1')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->setMaxResults(1);
|
||||||
|
return (bool) $qb->execute()->fetchOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws DatabaseConnectionException
|
||||||
|
* @throws DatabaseErrorException
|
||||||
|
*/
|
||||||
|
public function createSeoUrl()
|
||||||
|
{
|
||||||
|
$query = "INSERT INTO `oxseo` (`OXOBJECTID`, `OXIDENT`, `OXSHOPID`, `OXLANG`, `OXSTDURL`, `OXSEOURL`, `OXTYPE`, `OXFIXED`, `OXEXPIRED`, `OXPARAMS`, `OXTIMESTAMP`) VALUES
|
||||||
|
('ff57646b47249ee33c6b672741ac371a', 'bd3b6183c9a2f94682f4c62e714e4d6b', 1, 1, 'index.php?cl=d3_account_webauthn', 'en/key-authentication/', 'static', 0, 0, '', NOW()),
|
||||||
|
('ff57646b47249ee33c6b672741ac371a', '94d0d3ec07f10e8838a71e54084be885', 1, 0, 'index.php?cl=d3_account_webauthn', 'sicherheitsschluessel/', 'static', 0, 0, '', NOW());";
|
||||||
|
|
||||||
|
DatabaseProvider::getDb()->execute($query);
|
||||||
|
}
|
||||||
|
}
|
@ -15,44 +15,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace D3\Webauthn\Setup;
|
namespace D3\Webauthn\Setup;
|
||||||
|
|
||||||
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
|
|
||||||
use Doctrine\DBAL\Exception as DoctrineException;
|
|
||||||
use Doctrine\DBAL\Query\QueryBuilder;
|
|
||||||
use Exception;
|
|
||||||
use OxidEsales\Eshop\Core\DatabaseProvider;
|
|
||||||
use OxidEsales\Eshop\Core\DbMetaDataHandler;
|
|
||||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||||
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
|
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
|
||||||
use OxidEsales\Eshop\Core\Registry;
|
|
||||||
use OxidEsales\Eshop\Core\UtilsView;
|
|
||||||
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
|
|
||||||
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
|
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
|
||||||
|
|
||||||
class Events
|
class Events
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* SQL statement, that will be executed only at the first time of module installation.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $_createCredentialSql =
|
|
||||||
"CREATE TABLE `d3wa_usercredentials` (
|
|
||||||
`OXID` char(32) NOT NULL,
|
|
||||||
`OXUSERID` char(32) NOT NULL,
|
|
||||||
`OXSHOPID` int(11) NOT NULL,
|
|
||||||
`NAME` varchar(100) NOT NULL,
|
|
||||||
`CREDENTIALID` char(128) NOT NULL,
|
|
||||||
`CREDENTIAL` varchar(2000) NOT NULL,
|
|
||||||
`OXTIMESTAMP` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
|
||||||
PRIMARY KEY (`OXID`),
|
|
||||||
KEY `CREDENTIALID_IDX` (`CREDENTIALID`),
|
|
||||||
KEY `SHOPUSER_IDX` (`OXUSERID`,`OXSHOPID`) USING BTREE
|
|
||||||
) ENGINE=InnoDB COMMENT='WebAuthn Credentials';";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute action on activate event
|
* Execute action on activate event
|
||||||
|
* @codeCoverageIgnore
|
||||||
* @return void
|
* @return void
|
||||||
* @throws DatabaseConnectionException
|
* @throws DatabaseConnectionException
|
||||||
* @throws DatabaseErrorException
|
* @throws DatabaseErrorException
|
||||||
@ -63,158 +33,18 @@ class Events
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::setupModule();
|
$actions = oxNew(Actions::class);
|
||||||
|
$actions->setupModule();
|
||||||
self::regenerateViews();
|
$actions->regenerateViews();
|
||||||
|
$actions->clearCache();
|
||||||
self::clearCache();
|
$actions->seoUrl();
|
||||||
|
|
||||||
self::seoUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function onDeactivate()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the sql at the first time of the module installation.
|
|
||||||
* @return void
|
|
||||||
* @throws DatabaseConnectionException
|
|
||||||
* @throws DatabaseErrorException
|
|
||||||
*/
|
|
||||||
private static function setupModule()
|
|
||||||
{
|
|
||||||
if (!self::tableExists('d3wa_usercredentials')) {
|
|
||||||
self::executeSQL(self::$_createCredentialSql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if table exists
|
|
||||||
*
|
|
||||||
* @param string $sTableName table name
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function tableExists(string $sTableName): bool
|
|
||||||
{
|
|
||||||
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
|
||||||
|
|
||||||
return $oDbMetaDataHandler->tableExists($sTableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes given sql statement.
|
|
||||||
*
|
|
||||||
* @param string $sSQL Sql to execute.
|
|
||||||
* @throws DatabaseConnectionException
|
|
||||||
* @throws DatabaseErrorException
|
|
||||||
*/
|
|
||||||
private static function executeSQL($sSQL)
|
|
||||||
{
|
|
||||||
DatabaseProvider::getDb()->execute($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if field exists in table
|
|
||||||
*
|
|
||||||
* @param string $sFieldName field name
|
|
||||||
* @param string $sTableName table name
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function fieldExists(string $sFieldName, string $sTableName): bool
|
|
||||||
{
|
|
||||||
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
|
||||||
|
|
||||||
return $oDbMetaDataHandler->fieldExists($sFieldName, $sTableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Regenerate views for changed tables
|
|
||||||
*/
|
|
||||||
protected static function regenerateViews()
|
|
||||||
{
|
|
||||||
$oDbMetaDataHandler = oxNew(DbMetaDataHandler::class );
|
|
||||||
$oDbMetaDataHandler->updateViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clear cache
|
|
||||||
*/
|
|
||||||
private static function clearCache()
|
|
||||||
{
|
|
||||||
/** @var UtilsView $oUtilsView */
|
|
||||||
$oUtilsView = Registry::getUtilsView();
|
|
||||||
$sSmartyDir = $oUtilsView->getSmartyDir();
|
|
||||||
|
|
||||||
if ($sSmartyDir && is_readable($sSmartyDir)) {
|
|
||||||
foreach (glob($sSmartyDir . '*') as $sFile) {
|
|
||||||
if (!is_dir($sFile)) {
|
|
||||||
@unlink($sFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function seoUrl()
|
public static function onDeactivate(): void
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
if (!self::hasSeoUrl()) {
|
|
||||||
self::createSeoUrl();
|
|
||||||
}
|
|
||||||
} catch (Exception|NotFoundExceptionInterface|DoctrineDriverException|ContainerExceptionInterface $e) {
|
|
||||||
Registry::getUtilsView()->addErrorToDisplay('error wile creating SEO URLs: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @throws DoctrineDriverException
|
|
||||||
* @throws DoctrineException
|
|
||||||
* @throws ContainerExceptionInterface
|
|
||||||
* @throws NotFoundExceptionInterface
|
|
||||||
*/
|
|
||||||
private static function hasSeoUrl(): bool
|
|
||||||
{
|
|
||||||
/** @var QueryBuilder $qb */
|
|
||||||
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
|
|
||||||
$qb->select('1')
|
|
||||||
->from('oxseo')
|
|
||||||
->where(
|
|
||||||
$qb->expr()->and(
|
|
||||||
$qb->expr()->eq(
|
|
||||||
'oxstdurl',
|
|
||||||
$qb->createNamedParameter('index.php?cl=d3_account_webauthn')
|
|
||||||
),
|
|
||||||
$qb->expr()->eq(
|
|
||||||
'oxshopid',
|
|
||||||
$qb->createNamedParameter(Registry::getConfig()->getShopId())
|
|
||||||
),
|
|
||||||
$qb->expr()->eq(
|
|
||||||
'oxlang',
|
|
||||||
$qb->createNamedParameter('1')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
->setMaxResults(1);
|
|
||||||
return (bool) $qb->execute()->fetchOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
* @throws DatabaseConnectionException
|
|
||||||
* @throws DatabaseErrorException
|
|
||||||
*/
|
|
||||||
private static function createSeoUrl()
|
|
||||||
{
|
|
||||||
$query = "INSERT INTO `oxseo` (`OXOBJECTID`, `OXIDENT`, `OXSHOPID`, `OXLANG`, `OXSTDURL`, `OXSEOURL`, `OXTYPE`, `OXFIXED`, `OXEXPIRED`, `OXPARAMS`, `OXTIMESTAMP`) VALUES
|
|
||||||
('ff57646b47249ee33c6b672741ac371a', 'bd3b6183c9a2f94682f4c62e714e4d6b', 1, 1, 'index.php?cl=d3_account_webauthn', 'en/key-authentication/', 'static', 0, 0, '', NOW()),
|
|
||||||
('ff57646b47249ee33c6b672741ac371a', '94d0d3ec07f10e8838a71e54084be885', 1, 0, 'index.php?cl=d3_account_webauthn', 'sicherheitsschluessel/', 'static', 0, 0, '', NOW());";
|
|
||||||
|
|
||||||
DatabaseProvider::getDb()->execute($query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,13 +16,21 @@ declare(strict_types=1);
|
|||||||
namespace D3\Webauthn\tests\unit\Modules\Application\Component;
|
namespace D3\Webauthn\tests\unit\Modules\Application\Component;
|
||||||
|
|
||||||
use D3\TestingTools\Development\CanAccessRestricted;
|
use D3\TestingTools\Development\CanAccessRestricted;
|
||||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnException;
|
|
||||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
|
use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
|
||||||
use D3\Webauthn\Application\Model\Exceptions\WebauthnLoginErrorException;
|
use D3\Webauthn\Application\Model\Exceptions\WebauthnLoginErrorException;
|
||||||
|
use D3\Webauthn\Application\Model\Webauthn;
|
||||||
|
use D3\Webauthn\Application\Model\WebauthnConf;
|
||||||
use D3\Webauthn\Application\Model\WebauthnLogin;
|
use D3\Webauthn\Application\Model\WebauthnLogin;
|
||||||
|
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent;
|
||||||
|
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent_parent;
|
||||||
use OxidEsales\Eshop\Application\Component\UserComponent;
|
use OxidEsales\Eshop\Application\Component\UserComponent;
|
||||||
|
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
|
||||||
|
use OxidEsales\Eshop\Application\Model\User;
|
||||||
|
use OxidEsales\Eshop\Core\Controller\BaseController;
|
||||||
use OxidEsales\Eshop\Core\Registry;
|
use OxidEsales\Eshop\Core\Registry;
|
||||||
|
use OxidEsales\Eshop\Core\Request;
|
||||||
use OxidEsales\Eshop\Core\Session;
|
use OxidEsales\Eshop\Core\Session;
|
||||||
|
use OxidEsales\Eshop\Core\Utils;
|
||||||
use OxidEsales\Eshop\Core\UtilsView;
|
use OxidEsales\Eshop\Core\UtilsView;
|
||||||
use OxidEsales\TestingLibrary\UnitTestCase;
|
use OxidEsales\TestingLibrary\UnitTestCase;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
@ -32,6 +40,273 @@ class UserComponentWebauthnTest extends UnitTestCase
|
|||||||
{
|
{
|
||||||
use CanAccessRestricted;
|
use CanAccessRestricted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent::login
|
||||||
|
*/
|
||||||
|
public function canLogin()
|
||||||
|
{
|
||||||
|
/** @var d3_webauthn_UserComponent|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(UserComponent::class)
|
||||||
|
->onlyMethods(['d3CallMockableFunction', 'd3WebauthnLogin'])
|
||||||
|
->getMock();
|
||||||
|
$sut->expects($this->once())->method('d3CallMockableFunction')->with(
|
||||||
|
$this->identicalTo([d3_webauthn_UserComponent_parent::class, 'login'])
|
||||||
|
);
|
||||||
|
$sut->expects($this->once())->method('d3WebauthnLogin');
|
||||||
|
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'login'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @param $canUseWebauthn
|
||||||
|
* @param $loggedin
|
||||||
|
* @param $setVariableCount
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @dataProvider canCheckloginDataProvider
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent::d3WebauthnLogin
|
||||||
|
*/
|
||||||
|
public function canWebauthnLogin($canUseWebauthn, $loggedin, $setVariableCount, $doRedirect)
|
||||||
|
{
|
||||||
|
/** @var Utils|MockObject $utilsMock */
|
||||||
|
$utilsMock = $this->getMockBuilder(Utils::class)
|
||||||
|
->onlyMethods(['redirect'])
|
||||||
|
->getMock();
|
||||||
|
$utilsMock->expects($this->exactly((int) $doRedirect))->method('redirect');
|
||||||
|
|
||||||
|
/** @var BaseController|MockObject $baseControllerMock */
|
||||||
|
$baseControllerMock = $this->getMockBuilder(BaseController::class)
|
||||||
|
->addMethods(['getNavigationParams'])
|
||||||
|
->getMock();
|
||||||
|
$baseControllerMock->method('getNavigationParams')->willReturn(['empty']);
|
||||||
|
|
||||||
|
/** @var Request|MockObject $requestMock */
|
||||||
|
$requestMock = $this->getMockBuilder(Request::class)
|
||||||
|
->onlyMethods(['getRequestParameter'])
|
||||||
|
->getMock();
|
||||||
|
$requestMock->method('getRequestParameter')->willReturnMap([
|
||||||
|
['lgn_usr', 'myUserName']
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var User|MockObject $userMock */
|
||||||
|
$userMock = $this->getMockBuilder(User::class)
|
||||||
|
->onlyMethods(['d3GetLoginUserId'])
|
||||||
|
->getMock();
|
||||||
|
$userMock->method('d3GetLoginUserId')->willReturn('myUserId');
|
||||||
|
|
||||||
|
/** @var Session|MockObject $sessionMock */
|
||||||
|
$sessionMock = $this->getMockBuilder(Session::class)
|
||||||
|
->onlyMethods(['setVariable', 'getVariable'])
|
||||||
|
->getMock();
|
||||||
|
$sessionMock->expects($this->exactly($setVariableCount))->method('setVariable');
|
||||||
|
$sessionMock->method('getVariable')->with(WebauthnConf::WEBAUTHN_ADMIN_SESSION_LOGINUSER)
|
||||||
|
->willReturn('myUserName');
|
||||||
|
|
||||||
|
/** @var d3_webauthn_UserComponent|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(UserComponent::class)
|
||||||
|
->onlyMethods(['d3CanUseWebauthn', 'd3CallMockableFunction', 'd3HasWebauthnButNotLoggedin',
|
||||||
|
'd3GetMockableOxNewObject', 'd3GetMockableRegistryObject', 'getParent'
|
||||||
|
])
|
||||||
|
->getMock();
|
||||||
|
$sut->method('d3CanUseWebauthn')->willReturn($canUseWebauthn);
|
||||||
|
$sut->method('d3CallMockableFunction')->willReturn('parentReturn');
|
||||||
|
$sut->method('d3HasWebauthnButNotLoggedin')->willReturn($loggedin);
|
||||||
|
$sut->method('d3GetMockableOxNewObject')->willReturnCallback(
|
||||||
|
function () use ($userMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case User::class:
|
||||||
|
return $userMock;
|
||||||
|
default:
|
||||||
|
return call_user_func_array("oxNew", $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
function () use ($utilsMock, $requestMock, $sessionMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Utils::class:
|
||||||
|
return $utilsMock;
|
||||||
|
case Request::class:
|
||||||
|
return $requestMock;
|
||||||
|
case Session::class:
|
||||||
|
return $sessionMock;
|
||||||
|
default:
|
||||||
|
return Registry::get($args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$sut->method('getParent')->willReturn($baseControllerMock);
|
||||||
|
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'd3WebauthnLogin'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function canCheckloginDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'can not use webauthn' => [false, false, 0, false],
|
||||||
|
'already logged in' => [true, false, 0, false],
|
||||||
|
'passed' => [true, true, 4, true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @param $username
|
||||||
|
* @param $userId
|
||||||
|
* @param $hasWebauthnLogin
|
||||||
|
* @param $usedPassword
|
||||||
|
* @param $expected
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @dataProvider canUseWebauthnDataProvider
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent::d3CanUseWebauthn
|
||||||
|
*/
|
||||||
|
public function canUseWebauthn($username, $userId, $hasWebauthnLogin, $usedPassword, $expected)
|
||||||
|
{
|
||||||
|
/** @var Session|MockObject $sessionMock */
|
||||||
|
$sessionMock = $this->getMockBuilder(Session::class)
|
||||||
|
->onlyMethods(['hasVariable'])
|
||||||
|
->getMock();
|
||||||
|
$sessionMock->method('hasVariable')->with(WebauthnConf::WEBAUTHN_SESSION_AUTH)
|
||||||
|
->willReturn($hasWebauthnLogin);
|
||||||
|
|
||||||
|
/** @var Request|MockObject $requestMock */
|
||||||
|
$requestMock = $this->getMockBuilder(Request::class)
|
||||||
|
->onlyMethods(['getRequestParameter'])
|
||||||
|
->getMock();
|
||||||
|
$requestMock->method('getRequestParameter')->with('lgn_pwd')->willReturn($usedPassword);
|
||||||
|
|
||||||
|
/** @var d3_webauthn_UserComponent|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(UserComponent::class)
|
||||||
|
->onlyMethods(['d3GetMockableRegistryObject'])
|
||||||
|
->getMock();
|
||||||
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
function () use ($requestMock, $sessionMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Request::class:
|
||||||
|
return $requestMock;
|
||||||
|
case Session::class:
|
||||||
|
return $sessionMock;
|
||||||
|
default:
|
||||||
|
return Registry::get($args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'd3CanUseWebauthn',
|
||||||
|
[$username, $userId]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function canUseWebauthnDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'no username' => [null, 'myUserId', false, null, false],
|
||||||
|
'no userid' => ['username', null, false, null, false],
|
||||||
|
'existing webauthn login' => ['username', 'myUserId', true, null, false],
|
||||||
|
'used password' => ['username', 'myUserId', false, 'myPassword', false],
|
||||||
|
'passed' => ['username', 'myUserId', false, null, true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @param $webauthnActive
|
||||||
|
* @param $hasAuth
|
||||||
|
* @param $expected
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent::d3HasWebauthnButNotLoggedin
|
||||||
|
* @dataProvider canHasWebauthnButNotLoggedinDataProvider
|
||||||
|
*/
|
||||||
|
public function canHasWebauthnButNotLoggedin($webauthnActive, $hasAuth, $expected)
|
||||||
|
{
|
||||||
|
/** @var Session|MockObject $sessionMock */
|
||||||
|
$sessionMock = $this->getMockBuilder(Session::class)
|
||||||
|
->onlyMethods(['getVariable'])
|
||||||
|
->getMock();
|
||||||
|
$sessionMock->method('getVariable')->with(WebauthnConf::WEBAUTHN_SESSION_AUTH)
|
||||||
|
->willReturn($hasAuth);
|
||||||
|
|
||||||
|
/** @var Webauthn|MockObject $webauthnMock */
|
||||||
|
$webauthnMock = $this->getMockBuilder(Webauthn::class)
|
||||||
|
->onlyMethods(['isActive'])
|
||||||
|
->getMock();
|
||||||
|
$webauthnMock->method('isActive')->willReturn($webauthnActive);
|
||||||
|
|
||||||
|
/** @var UserComponent|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(UserComponent::class)
|
||||||
|
->onlyMethods(['d3GetMockableOxNewObject', 'd3GetMockableRegistryObject'])
|
||||||
|
->getMock();
|
||||||
|
$sut->method('d3GetMockableOxNewObject')->willReturnCallback(
|
||||||
|
function () use ($webauthnMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Webauthn::class:
|
||||||
|
return $webauthnMock;
|
||||||
|
default:
|
||||||
|
return call_user_func_array("oxNew", $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
function () use ($sessionMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Session::class:
|
||||||
|
return $sessionMock;
|
||||||
|
default:
|
||||||
|
return Registry::get($args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'd3HasWebauthnButNotLoggedin',
|
||||||
|
['userId']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function canHasWebauthnButNotLoggedinDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'webauthn not active' => [false, false, false],
|
||||||
|
'has webauthn auth' => [true, true, false],
|
||||||
|
'passed' => [true, false, true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @return void
|
* @return void
|
||||||
|
@ -16,7 +16,13 @@ declare(strict_types=1);
|
|||||||
namespace D3\Webauthn\tests\unit\Modules\Application\Model;
|
namespace D3\Webauthn\tests\unit\Modules\Application\Model;
|
||||||
|
|
||||||
use D3\TestingTools\Development\CanAccessRestricted;
|
use D3\TestingTools\Development\CanAccessRestricted;
|
||||||
|
use D3\Webauthn\Application\Model\WebauthnConf;
|
||||||
|
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
|
||||||
|
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn_parent;
|
||||||
|
use Exception;
|
||||||
use OxidEsales\Eshop\Application\Model\User;
|
use OxidEsales\Eshop\Application\Model\User;
|
||||||
|
use OxidEsales\Eshop\Core\Config;
|
||||||
|
use OxidEsales\Eshop\Core\Exception\UserException;
|
||||||
use OxidEsales\Eshop\Core\Registry;
|
use OxidEsales\Eshop\Core\Registry;
|
||||||
use OxidEsales\Eshop\Core\Session;
|
use OxidEsales\Eshop\Core\Session;
|
||||||
use OxidEsales\TestingLibrary\UnitTestCase;
|
use OxidEsales\TestingLibrary\UnitTestCase;
|
||||||
@ -27,6 +33,34 @@ class UserWebauthnTest extends UnitTestCase
|
|||||||
{
|
{
|
||||||
use CanAccessRestricted;
|
use CanAccessRestricted;
|
||||||
|
|
||||||
|
protected $userId = 'userIdFixture';
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
/** @var d3_User_Webauthn $user */
|
||||||
|
$user = oxNew(User::class);
|
||||||
|
$user->setId($this->userId);
|
||||||
|
$user->assign([
|
||||||
|
'oxusername' => 'userNameFixture',
|
||||||
|
'oxshopid' => '15',
|
||||||
|
'oxrights' => 'user',
|
||||||
|
]);
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown(): void
|
||||||
|
{
|
||||||
|
parent::tearDown();
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var d3_User_Webauthn $user */
|
||||||
|
$user = oxNew(User::class);
|
||||||
|
$user->delete($this->userId);
|
||||||
|
} catch (Exception $e) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @return void
|
* @return void
|
||||||
@ -34,6 +68,27 @@ class UserWebauthnTest extends UnitTestCase
|
|||||||
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::logout
|
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::logout
|
||||||
*/
|
*/
|
||||||
public function canLogout()
|
public function canLogout()
|
||||||
|
{
|
||||||
|
/** @var User|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(User::class)
|
||||||
|
->onlyMethods(['d3CallMockableFunction', 'd3WebauthnLogout'])
|
||||||
|
->getMock();
|
||||||
|
$sut->expects($this->once())->method('d3CallMockableFunction')->willReturn(true);
|
||||||
|
$sut->expects($this->once())->method('d3WebauthnLogout');
|
||||||
|
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'logout'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::d3WebauthnLogout
|
||||||
|
*/
|
||||||
|
public function canWebauthnLogout()
|
||||||
{
|
{
|
||||||
/** @var Session|MockObject $sessionMock */
|
/** @var Session|MockObject $sessionMock */
|
||||||
$sessionMock = $this->getMockBuilder(Session::class)
|
$sessionMock = $this->getMockBuilder(Session::class)
|
||||||
@ -43,9 +98,8 @@ class UserWebauthnTest extends UnitTestCase
|
|||||||
|
|
||||||
/** @var User|MockObject $sut */
|
/** @var User|MockObject $sut */
|
||||||
$sut = $this->getMockBuilder(User::class)
|
$sut = $this->getMockBuilder(User::class)
|
||||||
->onlyMethods(['d3CallMockableFunction', 'd3GetMockableRegistryObject'])
|
->onlyMethods(['d3GetMockableRegistryObject'])
|
||||||
->getMock();
|
->getMock();
|
||||||
$sut->method('d3CallMockableFunction')->willReturn(true);
|
|
||||||
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
function () use ($sessionMock) {
|
function () use ($sessionMock) {
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
@ -60,7 +114,165 @@ class UserWebauthnTest extends UnitTestCase
|
|||||||
|
|
||||||
$this->callMethod(
|
$this->callMethod(
|
||||||
$sut,
|
$sut,
|
||||||
'logout'
|
'd3WebauthnLogout'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::login
|
||||||
|
*/
|
||||||
|
public function canLogin()
|
||||||
|
{
|
||||||
|
/** @var User|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(User::class)
|
||||||
|
->onlyMethods(['d3CallMockableFunction', 'd3WebauthnLogin'])
|
||||||
|
->getMock();
|
||||||
|
$sut->expects($this->once())->method('d3CallMockableFunction')->with($this->identicalTo(
|
||||||
|
[d3_User_Webauthn_parent::class, 'login']
|
||||||
|
))->willReturn(true);
|
||||||
|
$sut->expects($this->once())->method('d3WebauthnLogin')->willReturn(true);
|
||||||
|
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'login',
|
||||||
|
['myUserName', 'myPassword']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::d3WebauthnLogin
|
||||||
|
* @dataProvider canWebauthnLoginDataProvider
|
||||||
|
*/
|
||||||
|
public function canWebauthnLogin($authInSession, $userNameArgument, $userNameInSession, $canLoad, $userIsLoadable, $expected)
|
||||||
|
{
|
||||||
|
/** @var Config|MockObject $configMock */
|
||||||
|
$configMock = $this->getMockBuilder(Config::class)
|
||||||
|
->onlyMethods(['getShopId'])
|
||||||
|
->getMock();
|
||||||
|
$configMock->method('getShopId')->willReturn(1);
|
||||||
|
|
||||||
|
/** @var Session|MockObject $sessionMock */
|
||||||
|
$sessionMock = $this->getMockBuilder(Session::class)
|
||||||
|
->onlyMethods(['getVariable'])
|
||||||
|
->getMock();
|
||||||
|
$sessionMock->method('getVariable')->willReturnMap([
|
||||||
|
[WebauthnConf::WEBAUTHN_SESSION_AUTH, $authInSession],
|
||||||
|
[WebauthnConf::WEBAUTHN_SESSION_LOGINUSER, $userNameInSession]
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** @var User|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(User::class)
|
||||||
|
->onlyMethods(['d3GetMockableRegistryObject', 'load'])
|
||||||
|
->getMock();
|
||||||
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
function () use ($sessionMock, $configMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Session::class:
|
||||||
|
return $sessionMock;
|
||||||
|
case Config::class:
|
||||||
|
return $configMock;
|
||||||
|
default:
|
||||||
|
return Registry::get($args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$sut->expects($this->exactly((int) ($canLoad)))->method('load')->will(
|
||||||
|
$userIsLoadable ?
|
||||||
|
$this->returnValue(true) :
|
||||||
|
$this->throwException(oxNew(UserException::class))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$userIsLoadable) {
|
||||||
|
$this->expectException(UserException::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'd3WebauthnLogin',
|
||||||
|
[$userNameArgument]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public function canWebauthnLoginDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'has no session auth' => [null, 'userArgument', null, false, true, 'userArgument'],
|
||||||
|
'different username' => ['sessionAuth', 'userArgument', 'sessionArgument', false, true, 'userArgument'],
|
||||||
|
'identical username' => ['sessionAuth', 'myUserName', 'myUserName', true, true, 'myUserName'],
|
||||||
|
'user not loadable' => ['sessionAuth', 'myUserName', 'myUserName', true, false, 'userSession'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @param $userName
|
||||||
|
* @param $shopId
|
||||||
|
* @param $rights
|
||||||
|
* @param $expected
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @dataProvider canGetLoginUserIdDataProvider
|
||||||
|
* @covers \D3\Webauthn\Modules\Application\Model\d3_User_Webauthn::d3GetLoginUserId
|
||||||
|
*/
|
||||||
|
public function canGetLoginUserId($userName, $shopId, $rights, $expected)
|
||||||
|
{
|
||||||
|
/** @var Config|MockObject $configMock */
|
||||||
|
$configMock = $this->getMockBuilder(Config::class)
|
||||||
|
->onlyMethods(['getShopId'])
|
||||||
|
->getMock();
|
||||||
|
$configMock->method('getShopId')->willReturn($shopId);
|
||||||
|
|
||||||
|
/** @var User|MockObject $sut */
|
||||||
|
$sut = $this->getMockBuilder(User::class)
|
||||||
|
->onlyMethods(['d3GetMockableRegistryObject'])
|
||||||
|
->getMock();
|
||||||
|
$sut->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
function () use ($configMock) {
|
||||||
|
$args = func_get_args();
|
||||||
|
switch ($args[0]) {
|
||||||
|
case Config::class:
|
||||||
|
return $configMock;
|
||||||
|
default:
|
||||||
|
return Registry::get($args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$this->callMethod(
|
||||||
|
$sut,
|
||||||
|
'd3GetLoginUserId',
|
||||||
|
[$userName, $rights]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public function canGetLoginUserIdDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'username not set' => [null, '15', 'user', null],
|
||||||
|
'user is loadable' => ['userNameFixture', '15', 'user', $this->userId],
|
||||||
|
'wrong shop id' => ['userNameFixture', '13', 'user', null],
|
||||||
|
'wrong rights' => ['userNameFixture', '15', 'foobar', null],
|
||||||
|
'no rights check' => ['userNameFixture', '15', null, $this->userId],
|
||||||
|
'user not loadable' => ['unknown', '15', '20', null],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user