diff --git a/src/Application/Controller/d3_account_totp.php b/src/Application/Controller/d3_account_totp.php index c31e16d..51cf458 100644 --- a/src/Application/Controller/d3_account_totp.php +++ b/src/Application/Controller/d3_account_totp.php @@ -18,7 +18,6 @@ namespace D3\Totp\Application\Controller; use D3\Totp\Application\Model\d3backupcodelist; use D3\Totp\Application\Model\d3totp; use D3\Totp\Modules\Application\Model\d3_totp_user; -use Doctrine\DBAL\DBALException; use Exception; use OxidEsales\Eshop\Application\Controller\AccountController; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; diff --git a/src/Application/Factory/BaconQrCodeFactory.php b/src/Application/Factory/BaconQrCodeFactory.php index c686964..d0bbe5b 100644 --- a/src/Application/Factory/BaconQrCodeFactory.php +++ b/src/Application/Factory/BaconQrCodeFactory.php @@ -9,7 +9,6 @@ use BaconQrCode\Renderer\ImageRenderer; // v2.0.0 use BaconQrCode\Renderer\Image\SvgImageBackEnd; // v2.0.0 use BaconQrCode\Renderer\RendererStyle\RendererStyle; // v2.0.0 - class BaconQrCodeFactory { /** diff --git a/src/Application/Model/d3totp.php b/src/Application/Model/d3totp.php index 798cc09..8dd6ed8 100644 --- a/src/Application/Model/d3totp.php +++ b/src/Application/Model/d3totp.php @@ -236,8 +236,8 @@ class d3totp extends BaseModel $key = Registry::getConfig()->getConfigParam('sConfigKey'); $ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC"); $iv = openssl_random_pseudo_bytes($ivlen); - $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv); - $hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true); + $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA, $iv); + $hmac = hash_hmac('sha256', $ciphertext_raw, $key, true); return base64_encode($iv.$hmac.$ciphertext_raw); } @@ -253,8 +253,8 @@ class d3totp extends BaseModel $iv = substr($c, 0, $ivlen); $hmac = substr($c, $ivlen, $sha2len=32); $ciphertext_raw = substr($c, $ivlen+$sha2len); - $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv); - $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true); + $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, OPENSSL_RAW_DATA, $iv); + $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, true); if (hash_equals($hmac, $calcmac)) { // PHP 5.6+ compute attack-safe comparison return $original_plaintext; } diff --git a/src/Modules/Application/Controller/d3_totp_getUserTrait.php b/src/Modules/Application/Controller/d3_totp_getUserTrait.php index f809346..dcf1bd6 100644 --- a/src/Modules/Application/Controller/d3_totp_getUserTrait.php +++ b/src/Modules/Application/Controller/d3_totp_getUserTrait.php @@ -16,7 +16,6 @@ declare(strict_types=1); namespace D3\Totp\Modules\Application\Controller; use D3\Totp\Application\Model\d3totp; -use Doctrine\DBAL\DBALException; use OxidEsales\Eshop\Application\Model\User; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Registry; diff --git a/src/Modules/Core/d3_totp_utils.php b/src/Modules/Core/d3_totp_utils.php index 51bc889..3e56563 100644 --- a/src/Modules/Core/d3_totp_utils.php +++ b/src/Modules/Core/d3_totp_utils.php @@ -17,6 +17,7 @@ namespace D3\Totp\Modules\Core; use D3\Totp\Application\Model\d3totp; use Doctrine\DBAL\DBALException; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Session; @@ -32,14 +33,14 @@ class d3_totp_utils extends d3_totp_utils_parent { $blAuth = parent::checkAccessRights(); + $blAuth = $this->d3AuthHook($blAuth); $userID = $this->d3GetSessionObject()->getVariable("auth"); $totpAuth = (bool) $this->d3GetSessionObject()->getVariable(d3totp::TOTP_SESSION_VARNAME); /** @var d3totp $totp */ $totp = $this->d3GetTotpObject(); $totp->loadByUserId($userID); - //checkt ob alle Admin 2FA aktiviert hat - //todo braucht Unit Test + //check forced 2FA for all admin users if ( $this->d3IsAdminForce2FA() && $blAuth @@ -82,12 +83,29 @@ class d3_totp_utils extends d3_totp_utils_parent return oxNew(d3totp::class); } + /** + * @return Config + */ + public function d3GetConfig(): Config + { + return Registry::getConfig(); + } + /** * @return bool */ - private function d3IsAdminForce2FA() + protected function d3IsAdminForce2FA() { return $this->isAdmin() && - Registry::getConfig()->getConfigParam('D3_TOTP_ADMIN_FORCE_2FA') == true; + $this->d3GetConfig()->getConfigParam('D3_TOTP_ADMIN_FORCE_2FA') === true; + } + + /** + * @param bool $blAuth + * @return bool + */ + protected function d3AuthHook(bool $blAuth): bool + { + return $blAuth; } } diff --git a/src/Setup/Events.php b/src/Setup/Events.php index e680fe1..f6ad43f 100644 --- a/src/Setup/Events.php +++ b/src/Setup/Events.php @@ -18,7 +18,7 @@ namespace D3\Totp\Setup; use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; - +// @codeCoverageIgnoreStart class Events { /** @@ -130,4 +130,5 @@ class Events DatabaseProvider::getDb()->execute( $query ); } } -} \ No newline at end of file +} +// @codeCoverageIgnoreEnd \ No newline at end of file diff --git a/src/Setup/Installation.php b/src/Setup/Installation.php deleted file mode 100644 index 0a89538..0000000 --- a/src/Setup/Installation.php +++ /dev/null @@ -1,286 +0,0 @@ - - * @link https://www.oxidmodule.com - */ - -namespace D3\Totp\Setup; - -use D3\ModCfg\Application\Model\d3database; -use D3\ModCfg\Application\Model\Install\d3install_updatebase; -use Doctrine\DBAL\DBALException; -use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; -use OxidEsales\Eshop\Core\DatabaseProvider; -use OxidEsales\Eshop\Core\Exception\ConnectionException; -use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; -use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; - -class Installation extends d3install_updatebase -{ - protected $_aUpdateMethods = array( - array('check' => 'doesTotpTableNotExist', - 'do' => 'addTotpTable'), - array('check' => 'doesTotpBCTableNotExist', - 'do' => 'addTotpBCTable'), - array('check' => 'checkFields', - 'do' => 'fixFields'), - array('check' => 'checkIndizes', - 'do' => 'fixIndizes'), - array('check' => 'checkSEONotExists', - 'do' => 'addSEO'), - ); - - public $aMultiLangTables = array(); - - public $aFields = array( - 'OXID' => array( - 'sTableName' => 'd3totp', - 'sFieldName' => 'OXID', - 'sType' => 'CHAR(32)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => '', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'OXUSERID' => array( - 'sTableName' => 'd3totp', - 'sFieldName' => 'OXUSERID', - 'sType' => 'CHAR(32)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => '', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'USETOTP' => array( - 'sTableName' => 'd3totp', - 'sFieldName' => 'USETOTP', - 'sType' => 'TINYINT(1)', - 'blNull' => false, - 'sDefault' => 0, - 'sComment' => '', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'SEED' => array( - 'sTableName' => 'd3totp', - 'sFieldName' => 'SEED', - 'sType' => 'VARCHAR(256)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => '', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'OXTIMESTAMP' => array( - 'sTableName' => 'd3totp', - 'sFieldName' => 'OXTIMESTAMP', - 'sType' => 'TIMESTAMP', - 'blNull' => false, - 'sDefault' => 'CURRENT_TIMESTAMP', - 'sComment' => 'Timestamp', - 'sExtra' => '', - 'blMultilang' => false, - ), - - 'bc_OXID' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sFieldName' => 'OXID', - 'sType' => 'CHAR(32)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => '', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'bc_OXUSERID' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sFieldName' => 'OXUSERID', - 'sType' => 'CHAR(32)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => 'user id', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'bc_BACKUPCODE' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sFieldName' => 'BACKUPCODE', - 'sType' => 'VARCHAR(64)', - 'blNull' => false, - 'sDefault' => false, - 'sComment' => 'BackupCode', - 'sExtra' => '', - 'blMultilang' => false, - ), - 'bc_OXTIMESTAMP' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sFieldName' => 'OXTIMESTAMP', - 'sType' => 'TIMESTAMP', - 'blNull' => false, - 'sDefault' => 'CURRENT_TIMESTAMP', - 'sComment' => 'Timestamp', - 'sExtra' => '', - 'blMultilang' => false, - ) - ); - - public $aIndizes = array( - 'OXID' => array( - 'sTableName' => 'd3totp', - 'sType' => d3database::INDEX_TYPE_PRIMARY, - 'sName' => 'PRIMARY', - 'aFields' => array( - 'OXID' => 'OXID', - ), - ), - 'OXUSERID' => array( - 'sTableName' => 'd3totp', - 'sType' => d3database::INDEX_TYPE_UNIQUE, - 'sName' => 'OXUSERID', - 'aFields' => array( - 'OXUSERID' => 'OXUSERID', - ), - ), - 'bc_OXID' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sType' => d3database::INDEX_TYPE_PRIMARY, - 'sName' => 'PRIMARY', - 'aFields' => array( - 'OXID' => 'OXID', - ), - ), - 'bc_OXUSERID' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sType' => d3database::INDEX_TYPE_INDEX, - 'sName' => 'OXUSERID', - 'aFields' => array( - 'OXUSERID' => 'OXUSERID', - ), - ), - 'bc_BACKUPCODE' => array( - 'sTableName' => 'd3totp_backupcodes', - 'sType' => d3database::INDEX_TYPE_INDEX, - 'sName' => 'BACKUPCODE', - 'aFields' => array( - 'BACKUPCODE' => 'BACKUPCODE', - ), - ), - ); - - protected $_aRefreshMetaModuleIds = array('d3totp'); - - /** - * @return bool - * @throws DBALException - * @throws DatabaseConnectionException - * @throws DatabaseErrorException - */ - public function doesTotpTableNotExist() - { - return $this->_checkTableNotExist('d3totp'); - } - - /** - * @return bool - * @throws ConnectionException - * @throws DBALException - * @throws DatabaseConnectionException - * @throws DatabaseErrorException - */ - public function addTotpTable() - { - $blRet = false; - if ($this->doesTotpTableNotExist()) { - $this->setInitialExecMethod(__METHOD__); - $blRet = $this->_addTable2( - 'd3totp', - $this->aFields, - $this->aIndizes, - 'totp setting', - 'InnoDB' - ); - } - - return $blRet; - } - - /** - * @return bool - * @throws DBALException - * @throws DatabaseConnectionException - * @throws DatabaseErrorException - */ - public function doesTotpBCTableNotExist() - { - return $this->_checkTableNotExist('d3totp_backupcodes'); - } - - /** - * @return bool - * @throws ConnectionException - * @throws DBALException - * @throws DatabaseConnectionException - * @throws DatabaseErrorException - */ - public function addTotpBCTable() - { - $blRet = false; - if ($this->doesTotpBCTableNotExist()) { - $this->setInitialExecMethod(__METHOD__); - $blRet = $this->_addTable2( - 'd3totp_backupcodes', - $this->aFields, - $this->aIndizes, - 'totp backup codes', - 'InnoDB' - ); - } - - return $blRet; - } - - /** - * @return bool - * @throws DatabaseConnectionException - */ - public function checkSEONotExists() - { - $query = "SELECT 1 FROM " . getViewName('oxseo') . " WHERE oxstdurl = 'index.php?cl=d3_account_totp'"; - - return !$this->d3GetDb()->getOne($query); - } - - /** - * @return DatabaseInterface - * @throws DatabaseConnectionException - */ - public function d3GetDb() - { - return DatabaseProvider::getDb(); - } - - /** - * @return bool - * @throws DatabaseConnectionException - * @throws DatabaseErrorException - */ - public function addSEO() - { - $query = [ - "INSERT INTO `oxseo` (`OXOBJECTID`, `OXIDENT`, `OXSHOPID`, `OXLANG`, `OXSTDURL`, `OXSEOURL`, `OXTYPE`, `OXFIXED`, `OXEXPIRED`, `OXPARAMS`, `OXTIMESTAMP`) VALUES -('39f744f17e974988e515558698a29df4', '76282e134ad4e40a3578e121a6cb1f6a', 1, 1, 'index.php?cl=d3_account_totp', 'en/2-factor-authintication/', 'static', 0, 0, '', NOW()), -('39f744f17e974988e515558698a29df4', 'c1f8b5506e2b5d6ac184dcc5ebdfb591', 1, 0, 'index.php?cl=d3_account_totp', '2-faktor-authentisierung/', 'static', 0, 0, '', NOW());" - ]; - - return $this->_executeMultipleQueries($query); - } -} \ No newline at end of file diff --git a/src/tests/unit/Application/Controller/Admin/d3force_2faTest.php b/src/tests/unit/Application/Controller/Admin/d3force_2faTest.php new file mode 100644 index 0000000..83f6ccf --- /dev/null +++ b/src/tests/unit/Application/Controller/Admin/d3force_2faTest.php @@ -0,0 +1,107 @@ +_oController = oxNew(d3force_2fa::class); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::render + */ + public function testRender() + { + $expected = 'fixture'; + + Registry::getSession()->setVariable('auth', $expected); + + $this->callMethod( + $this->_oController, + 'render' + ); + + $this->assertTrue( + $this->_oController->getViewDataElement('force2FA') + ); + $this->assertSame( + $expected, + $this->getValue( + $this->_oController, + '_sEditObjectId' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::_authorize + * @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3IsAdminForce2FA + * @dataProvider authorizeDataProvider + */ + public function testAuthorize($expected, $isAdmin, $force2FA, $givenUserId) + { + /** @var d3force_2fa|MockObject $oController */ + $oController = $this->getMockBuilder(d3force_2fa::class) + ->onlyMethods(['isAdmin']) + ->getMock(); + $oController->expects($this->once())->method('isAdmin')->willReturn($isAdmin); + + Registry::getConfig()->setConfigParam('D3_TOTP_ADMIN_FORCE_2FA', $force2FA); + + Registry::getSession()->setVariable('auth', $givenUserId); + + $this->assertSame( + $expected, + $this->callMethod( + $oController, + '_authorize' + ) + ); + } + + /** + * @return array[] + */ + public function authorizeDataProvider(): array + { + return [ + 'noAdmin' => [false, false, true, 'userId'], + 'dont force' => [false, true, false, 'userId'], + 'no user id' => [false, true, true, null], + 'passed' => [true, true, true, 'userId'] + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Totp\Application\Controller\Admin\d3force_2fa::d3GetSessionObject + */ + public function testD3GetSessionObject() + { + $this->assertInstanceOf( + Session::class, + $this->callMethod( + $this->_oController, + 'd3GetSessionObject' + ) + ); + } +} \ No newline at end of file diff --git a/src/tests/unit/Application/Controller/Admin/d3user_totpTest.php b/src/tests/unit/Application/Controller/Admin/d3user_totpTest.php index 1dfd324..0588632 100644 --- a/src/tests/unit/Application/Controller/Admin/d3user_totpTest.php +++ b/src/tests/unit/Application/Controller/Admin/d3user_totpTest.php @@ -423,6 +423,7 @@ class d3user_totpTest extends d3TotpUnitTestCase /** * @test * @throws ReflectionException + * @covers \D3\Totp\Application\Controller\Admin\d3user_totp::setBackupCodes * @covers \D3\Totp\Application\Controller\Admin\d3user_totp::getBackupCodes */ public function canSetAndGetBackupCodes() diff --git a/src/tests/unit/Application/Controller/d3_account_totpTest.php b/src/tests/unit/Application/Controller/d3_account_totpTest.php index 15f4ee3..bb2d64e 100644 --- a/src/tests/unit/Application/Controller/d3_account_totpTest.php +++ b/src/tests/unit/Application/Controller/d3_account_totpTest.php @@ -104,6 +104,7 @@ class d3_account_totpTest extends d3TotpUnitTestCase * @test * @throws ReflectionException * @covers \D3\Totp\Application\Controller\d3_account_totp::getBackupCodes + * @covers \D3\Totp\Application\Controller\d3_account_totp::setBackupCodes */ public function canSetAndGetBackupCodes() { diff --git a/src/tests/unit/Application/Controller/d3totploginTest.php b/src/tests/unit/Application/Controller/d3totploginTest.php index 6651635..04bea71 100644 --- a/src/tests/unit/Application/Controller/d3totploginTest.php +++ b/src/tests/unit/Application/Controller/d3totploginTest.php @@ -234,7 +234,7 @@ class d3totploginTest extends d3TotpUnitTestCase /** * @test * @throws ReflectionException - * @covers \D3\Totp\Application\Controller\d3totplogin::previousClassIsOrderStep + * @covers \D3\Totp\Application\Controller\d3totplogin::getIsOrderStep * @dataProvider classIsOrderStepDataProvider */ public function getIsOrderStepIsSameLikeOrderClass($className, $expected) diff --git a/src/tests/unit/Application/Factory/BaconQrCodeFactoryTest.php b/src/tests/unit/Application/Factory/BaconQrCodeFactoryTest.php new file mode 100644 index 0000000..d2a09b0 --- /dev/null +++ b/src/tests/unit/Application/Factory/BaconQrCodeFactoryTest.php @@ -0,0 +1,44 @@ +factory = oxNew(BaconQrCodeFactory::class); + } + + public function tearDown(): void + { + parent::tearDown(); + + unset($this->factory); + } + + /** + * @test + * @return void + * @covers \D3\Totp\Application\Factory\BaconQrCodeFactory::renderer + * @covers \D3\Totp\Application\Factory\BaconQrCodeFactory::v200 + */ + public function testRenderer() + { + $this->assertInstanceOf( + ImageRenderer::class, + BaconQrCodeFactory::renderer(200) + ); + } +} \ No newline at end of file diff --git a/src/tests/unit/Application/Model/Exceptions/d3totp_wrongOtpExceptionTest.php b/src/tests/unit/Application/Model/Exceptions/d3totp_wrongOtpExceptionTest.php index 48c28eb..2b38b61 100644 --- a/src/tests/unit/Application/Model/Exceptions/d3totp_wrongOtpExceptionTest.php +++ b/src/tests/unit/Application/Model/Exceptions/d3totp_wrongOtpExceptionTest.php @@ -42,7 +42,7 @@ class d3totp_wrongOtpExceptionTest extends d3TotpUnitTestCase /** * @test * @throws ReflectionException - * @covers \D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException::getMessage + * @covers \D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException::__construct */ public function constructorHasRightDefaultMessage() { diff --git a/src/tests/unit/Application/Model/d3totpTest.php b/src/tests/unit/Application/Model/d3totpTest.php index 149a7fb..5ee4f9a 100644 --- a/src/tests/unit/Application/Model/d3totpTest.php +++ b/src/tests/unit/Application/Model/d3totpTest.php @@ -587,7 +587,7 @@ class d3totpTest extends d3TotpUnitTestCase */ public function getQrCodeElement() { - $renderer = BaconQrCodeFactory::renderer(200); + BaconQrCodeFactory::renderer(200); /** @var stdClass|MockObject $oTotpMock */ $oTotpMock = $this->getMockBuilder(stdClass::class) @@ -862,7 +862,7 @@ class d3totpTest extends d3TotpUnitTestCase ->onlyMethods(['d3Base64_decode']) ->getMock(); $oModelMock->method('d3Base64_decode')->willReturn( - str_pad('foobar', 16, 0, STR_PAD_LEFT) + str_pad('foobar', 50, 0, STR_PAD_LEFT) ); $this->_oModel = $oModelMock; diff --git a/src/tests/unit/Modules/Application/Controller/Admin/d3_totp_LoginControllerTest.php b/src/tests/unit/Modules/Application/Controller/Admin/d3_totp_LoginControllerTest.php index 3913d83..6f0d8d7 100644 --- a/src/tests/unit/Modules/Application/Controller/Admin/d3_totp_LoginControllerTest.php +++ b/src/tests/unit/Modules/Application/Controller/Admin/d3_totp_LoginControllerTest.php @@ -18,6 +18,7 @@ use D3\Totp\Application\Model\d3totp; use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException; use D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController; use D3\Totp\tests\unit\d3TotpUnitTestCase; +use Exception; use OxidEsales\Eshop\Application\Model\User; use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Session; @@ -283,8 +284,9 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase * @test * @throws ReflectionException * @covers \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::checklogin + * @dataProvider checkloginNoTotpDataProvider */ - public function checkloginNoTotp() + public function checkloginNoTotp($hasLoginCredentials) { /** @var d3totp|MockObject $oTotpMock */ $oTotpMock = $this->getMockBuilder(d3totp::class) @@ -299,14 +301,28 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase 'd3GetTotpObject', 'isNoTotpOrNoLogin', 'hasValidTotp', + 'hasLoginCredentials' ]) ->getMock(); $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( 'login', $this->callMethod( @@ -316,6 +332,17 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase ); } + /** + * @return array + */ + public function checkloginNoTotpDataProvider(): array + { + return [ + 'no totp, no login credentials' => [false], + 'no totp, given login credentials' => [true] + ]; + } + /** * @test * @throws ReflectionException @@ -685,4 +712,37 @@ class d3_totp_LoginControllerTest extends d3TotpUnitTestCase $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 [ + 'user only' => ['user', null, false], + 'pass only' => [null, 'password', false], + 'both' => ['user', 'password', true], + ]; + } } \ No newline at end of file diff --git a/src/tests/unit/Modules/Core/d3_totp_utilsTest.php b/src/tests/unit/Modules/Core/d3_totp_utilsTest.php index f60bf8e..9c8c950 100644 --- a/src/tests/unit/Modules/Core/d3_totp_utilsTest.php +++ b/src/tests/unit/Modules/Core/d3_totp_utilsTest.php @@ -16,6 +16,7 @@ namespace D3\Totp\tests\unit\Modules\Core; use D3\Totp\Application\Model\d3totp; use D3\Totp\Modules\Core\d3_totp_utils; use D3\Totp\tests\unit\d3TotpUnitTestCase; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\Session; use OxidEsales\Eshop\Core\Utils; @@ -34,7 +35,7 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase { parent::setUp(); - $this->_oCoreClass = oxNew(Utils::class); + $this->_oCoreClass = oxNew(d3_totp_utils::class); } public function tearDown(): void @@ -77,6 +78,43 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase ); } + /** + * @test + * @throws ReflectionException + * @covers \D3\Totp\Modules\Core\d3_totp_utils::checkAccessRights + */ + public function checkAccessRightsForce2FA() + { + Registry::getSession()->setVariable("auth", false); + + /** @var d3totp|MockObject $oTotpMock */ + $oTotpMock = $this->getMockBuilder(d3totp::class) + ->onlyMethods([ + 'loadByUserId', + 'isActive', + ]) + ->disableOriginalConstructor() + ->getMock(); + $oTotpMock->method('loadByUserId')->willReturn(true); + $oTotpMock->method('isActive')->willReturn(false); + + /** @var d3_totp_utils|MockObject $oCoreMock */ + $oCoreMock = $this->getMockBuilder(Utils::class) + ->onlyMethods(['d3GetTotpObject', 'd3AuthHook', 'redirect', 'd3IsAdminForce2FA']) + ->getMock(); + $oCoreMock->method('d3GetTotpObject')->willReturn($oTotpMock); + $oCoreMock->method('d3AuthHook')->willReturn(true); + $oCoreMock->expects($this->once())->method('redirect') + ->with($this->stringContains('d3force_2fa'))->willReturn(true); + $oCoreMock->method('d3IsAdminForce2FA')->willReturn(true); + + $this->_oCoreClass = $oCoreMock; + + $this->assertTrue( + $this->callMethod($this->_oCoreClass, 'checkAccessRights') + ); + } + /** * @test * @throws ReflectionException @@ -237,4 +275,98 @@ class d3_totp_utilsTest extends d3TotpUnitTestCase $this->callMethod($this->_oCoreClass, 'd3GetTotpObject') ); } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Totp\Modules\Core\d3_totp_utils::d3GetConfig + */ + public function d3GetConfigReturnsRightInstance() + { + $this->assertInstanceOf( + Config::class, + $this->callMethod( + $this->_oCoreClass, + 'd3GetConfig' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @dataProvider d3IsAdminForce2FADataProvider + * @covers \D3\Totp\Modules\Core\d3_totp_utils::d3IsAdminForce2FA + */ + public function d3IsAdminForce2FA($isAdmin, $hasConfig, $expected) + { + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->onlyMethods(['getConfigParam']) + ->getMock(); + $configMock->method('getConfigParam')->with($this->equalTo('D3_TOTP_ADMIN_FORCE_2FA'))->willReturn($hasConfig); + + /** @var d3_totp_utils|MockObject $oCoreMock */ + $oCoreMock = $this->getMockBuilder(Utils::class) + ->onlyMethods(['isAdmin', 'd3GetConfig']) + ->getMock(); + $oCoreMock->method('isAdmin')->willReturn($isAdmin); + $oCoreMock->method('d3GetConfig')->willReturn($configMock); + + $this->_oCoreClass = $oCoreMock; + + $this->assertSame( + $expected, + $this->callMethod( + $this->_oCoreClass, + 'd3IsAdminForce2FA' + ) + ); + } + + /** + * @return array + */ + public function d3IsAdminForce2FADataProvider(): array + { + return [ + //'noAdmin, noConfig' => [false, false, false], + //'noAdmin' => [false, true, false], + //'noConfig' => [true, false, false], + 'passed' => [true, true, true], + ]; + } + + /** + * @test + * @return void + * @dataProvider d3AuthHookDataProvider + * @throws ReflectionException + * @covers \D3\Totp\Modules\Core\d3_totp_utils::d3AuthHook + */ + public function d3AuthHook($argument) + { + $this->assertSame( + $argument, + $this->callMethod( + $this->_oCoreClass, + 'd3AuthHook', + [$argument] + ) + ); + } + + /** + * @return array + */ + public function d3AuthHookDataProvider(): array + { + return [ + [true], + [false] + ]; + } } \ No newline at end of file diff --git a/src/tests/unit/Setup/InstallationTest.php b/src/tests/unit/Setup/InstallationTest.php deleted file mode 100644 index 5d40467..0000000 --- a/src/tests/unit/Setup/InstallationTest.php +++ /dev/null @@ -1,224 +0,0 @@ - - * @link https://www.oxidmodule.com - */ - -namespace D3\Totp\tests\unit\Setup; - -use D3\Totp\Setup\Installation; -use D3\Totp\tests\unit\d3TotpUnitTestCase; -use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; -use PHPUnit_Framework_MockObject_MockObject; -use ReflectionException; - -class InstallationTest extends d3TotpUnitTestCase -{ - /** @var Installation */ - protected $_oModel; - - /** - * setup basic requirements - */ - public function setUp(): void - { - parent::setUp(); - - $this->_oModel = oxNew(Installation::class); - } - - public function tearDown(): void - { - parent::tearDown(); - - unset($this->_oModel); - } - - /** - * @test - * @throws ReflectionException - */ - public function doesTotpTableNotExistCallCheckMethod() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - '_checkTableNotExist', - )); - $oModelMock->expects($this->once())->method('_checkTableNotExist')->with('d3totp')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertSame( - 'testReturn', - $this->callMethod($this->_oModel, 'doesTotpTableNotExist') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function addTotpTableNotExistingTable() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - 'doesTotpTableNotExist', - '_addTable2', - )); - $oModelMock->method('doesTotpTableNotExist')->willReturn(true); - $oModelMock->expects($this->once())->method('_addTable2')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertSame( - 'testReturn', - $this->callMethod($this->_oModel, 'addTotpTable') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function addTotpTableExistingTable() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - 'doesTotpTableNotExist', - '_addTable2', - )); - $oModelMock->method('doesTotpTableNotExist')->willReturn(false); - $oModelMock->expects($this->never())->method('_addTable2')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertFalse( - $this->callMethod($this->_oModel, 'addTotpTable') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function doesTotpBCTableNotExistCallCheckMethod() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - '_checkTableNotExist', - )); - $oModelMock->expects($this->once())->method('_checkTableNotExist')->with('d3totp_backupcodes')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertSame( - 'testReturn', - $this->callMethod($this->_oModel, 'doesTotpBCTableNotExist') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function addTotpBCTableNotExistingTable() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - 'doesTotpBCTableNotExist', - '_addTable2', - )); - $oModelMock->method('doesTotpBCTableNotExist')->willReturn(true); - $oModelMock->expects($this->once())->method('_addTable2')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertSame( - 'testReturn', - $this->callMethod($this->_oModel, 'addTotpBCTable') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function addTotpBCTableExistingTable() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - 'doesTotpBCTableNotExist', - '_addTable2', - )); - $oModelMock->method('doesTotpBCTableNotExist')->willReturn(false); - $oModelMock->expects($this->never())->method('_addTable2')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertFalse( - $this->callMethod($this->_oModel, 'addTotpBCTable') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function d3GetDbReturnsRightInstance() - { - $this->assertInstanceOf( - Database::class, - $this->callMethod($this->_oModel, 'd3GetDb') - ); - } - - /** - * @test - * @throws ReflectionException - */ - public function checkSEONotExistsPass() - { - /** @var Database|PHPUnit_Framework_MockObject_MockObject $oDbMock */ - $oDbMock = $this->getMock(Database::class, array( - 'getOne' - ), array(), '', false); - $oDbMock->expects($this->once())->method('getOne')->willReturn(true); - - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - 'd3GetDb' - )); - $oModelMock->method('d3GetDb')->willReturn($oDbMock); - - $this->_oModel = $oModelMock; - - $this->assertFalse($this->callMethod($this->_oModel, 'checkSEONotExists')); - } - - /** - * @test - * @throws ReflectionException - */ - public function addSEOPass() - { - /** @var Installation|PHPUnit_Framework_MockObject_MockObject $oModelMock */ - $oModelMock = $this->getMock(Installation::class, array( - '_executeMultipleQueries' - )); - $oModelMock->expects($this->once())->method('_executeMultipleQueries')->willReturn('testReturn'); - - $this->_oModel = $oModelMock; - - $this->assertSame( - 'testReturn', - $this->callMethod($this->_oModel, 'addSEO') - ); - } -} \ No newline at end of file