From ff9f1722afc04d18615b78e981c72e6b78d80a2e Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Mon, 5 Aug 2019 22:59:26 +0200 Subject: [PATCH] add test for totp login controller --- src/Application/Controller/d3totplogin.php | 25 +- .../translations/de/d3_totp_lang.php | 2 +- .../translations/en/d3_totp_lang.php | 2 +- .../views/admin/de/d3totp_lang.php | 2 +- .../views/admin/en/d3totp_lang.php | 2 +- .../Controller/d3totploginTest.php | 281 ++++++++++++++++++ 6 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 src/tests/unit/Application/Controller/d3totploginTest.php diff --git a/src/Application/Controller/d3totplogin.php b/src/Application/Controller/d3totplogin.php index 958934c..25a7c0b 100644 --- a/src/Application/Controller/d3totplogin.php +++ b/src/Application/Controller/d3totplogin.php @@ -20,6 +20,7 @@ use D3\Totp\Application\Model\d3totp; use OxidEsales\Eshop\Application\Controller\FrontendController; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Utils; class d3totplogin extends FrontendController { @@ -30,8 +31,10 @@ class d3totplogin extends FrontendController if (Registry::getSession()->hasVariable(d3totp::TOTP_SESSION_VARNAME) || false == Registry::getSession()->hasVariable(d3totp::TOTP_SESSION_CURRENTUSER) ) { - Registry::getUtils()->redirect('index.php?cl=start', true, 302); - exit; + $this->getUtils()->redirect('index.php?cl=start', true, 302); + if (false == defined('OXID_PHP_UNIT')) { + exit; + } } $this->addTplParam('navFormParams', Registry::getSession()->getVariable(d3totp::TOTP_SESSION_NAVFORMPARAMS)); @@ -39,13 +42,21 @@ class d3totplogin extends FrontendController return parent::render(); } + /** + * @return Utils + */ + public function getUtils() + { + return Registry::getUtils(); + } + /** * @return string|void * @throws DatabaseConnectionException */ public function getBackupCodeCountMessage() { - $oBackupCodeList = oxNew(d3backupcodelist::class); + $oBackupCodeList = $this->getBackupCodeListObject(); $iCount = $oBackupCodeList->getAvailableCodeCount(Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTUSER)); if ($iCount < 4) { @@ -58,6 +69,14 @@ class d3totplogin extends FrontendController return; } + /** + * @return d3backupcodelist + */ + public function getBackupCodeListObject() + { + return oxNew(d3backupcodelist::class); + } + public function getPreviousClass() { return Registry::getSession()->getVariable(d3totp::TOTP_SESSION_CURRENTCLASS); diff --git a/src/Application/translations/de/d3_totp_lang.php b/src/Application/translations/de/d3_totp_lang.php index 30705e1..9ff262d 100644 --- a/src/Application/translations/de/d3_totp_lang.php +++ b/src/Application/translations/de/d3_totp_lang.php @@ -44,7 +44,7 @@ $aLang = [ 'D3_TOTP_BACKUPCODES' => 'Backupcodes', 'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backupcodes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Authentisierung ändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.', - 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcodes verfügbar', + 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcode(s) verfügbar', 'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'Um neue Backupcodes zu erstellen, löschen Sie die bestehende Registrierung und legen diese bitte neu an.', 'D3_TOTP_ACCOUNT_SAVE' => 'Einstellungen übernehmen', diff --git a/src/Application/translations/en/d3_totp_lang.php b/src/Application/translations/en/d3_totp_lang.php index 1d6975a..3c20c19 100644 --- a/src/Application/translations/en/d3_totp_lang.php +++ b/src/Application/translations/en/d3_totp_lang.php @@ -44,7 +44,7 @@ $aLang = [ 'D3_TOTP_BACKUPCODES' => 'backup codes', 'D3_TOTP_BACKUPCODES_DESC' => 'You can use these backup codes to log on if it is not possible to generate the one-time password (e.g. device lost or newly installed). You can then change the settings to use 2-factor authentication or create a new account. Please save these codes securely at this moment. After leaving this page, these codes cannot be displayed again.', - 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'still %1$s backup codes available', + 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'still %1$s backup code(s) available', 'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'To create new backup codes, delete the existing registry and create a new one.', 'D3_TOTP_ACCOUNT_SAVE' => 'Confirm settings', diff --git a/src/Application/views/admin/de/d3totp_lang.php b/src/Application/views/admin/de/d3totp_lang.php index 0705402..e73777d 100644 --- a/src/Application/views/admin/de/d3totp_lang.php +++ b/src/Application/views/admin/de/d3totp_lang.php @@ -42,7 +42,7 @@ $aLang = [ 'D3_TOTP_BACKUPCODES' => 'Backupcodes', 'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backupcodes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Authentisierung ändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.', - 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcodes verfügbar', + 'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcode(s) verfügbar', 'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'Um neue Backupcodes zu erstellen, löschen Sie die bestehende Registrierung und legen diese bitte neu an.', 'D3_TOTP_SAVE' => 'Speichern', diff --git a/src/Application/views/admin/en/d3totp_lang.php b/src/Application/views/admin/en/d3totp_lang.php index 60ae1cd..48a77e9 100644 --- a/src/Application/views/admin/en/d3totp_lang.php +++ b/src/Application/views/admin/en/d3totp_lang.php @@ -42,7 +42,7 @@ $aLang = [ 'D3_TOTP_BACKUPCODES' => 'backup codes', 'D3_TOTP_BACKUPCODES_DESC' => 'You can use these backup codes to log on if it is not possible to generate the one-time password (e.g. device lost or newly installed). You can then change the settings to use 2-factor authentication or create a new 2FA login. Please save these codes safely at this moment. After leaving this page, these codes cannot be displayed again.', - 'D3_TOTP_AVAILBACKUPCODECOUNT' => '%1$s backup codes still available', + 'D3_TOTP_AVAILBACKUPCODECOUNT' => '%1$s backup code(s) still available', 'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'To create new backup codes, delete the existing registry and create a new one.', 'D3_TOTP_SAVE' => 'Save', diff --git a/src/tests/unit/Application/Controller/d3totploginTest.php b/src/tests/unit/Application/Controller/d3totploginTest.php new file mode 100644 index 0000000..c905217 --- /dev/null +++ b/src/tests/unit/Application/Controller/d3totploginTest.php @@ -0,0 +1,281 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\Totp\tests\unit\Application\Controller; + +use D3\Totp\Application\Controller\d3totplogin; +use D3\Totp\Application\Model\d3backupcodelist; +use D3\Totp\Application\Model\d3totp; +use D3\Totp\tests\unit\d3TotpUnitTestCase; +use Doctrine\DBAL\DBALException; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; +use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Utils; +use PHPUnit_Framework_MockObject_MockObject; +use ReflectionException; + +class d3totploginTest extends d3TotpUnitTestCase +{ + /** @var d3totplogin */ + protected $_oController; + + /** + * setup basic requirements + * @throws DBALException + * @throws DatabaseConnectionException + * @throws DatabaseErrorException + */ + public function setUp() + { + parent::setUp(); + + $this->_oController = oxNew(d3totplogin::class); + + Registry::getSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTUSER); + Registry::getSession()->deleteVariable(d3totp::TOTP_SESSION_CURRENTCLASS); + } + + public function tearDown() + { + parent::tearDown(); + + unset($this->_oController); + } + + /** + * @test + * @throws ReflectionException + */ + public function renderRedirectIfNoTotp() + { + /** @var Utils|PHPUnit_Framework_MockObject_MockObject $oUtilsMock */ + $oUtilsMock = $this->getMock(Utils::class, array( + 'redirect' + )); + $oUtilsMock->expects($this->once())->method('redirect')->willReturn(true); + + /** @var d3totplogin|PHPUnit_Framework_MockObject_MockObject $oControllerMock */ + $oControllerMock = $this->getMock(d3totplogin::class, array( + 'getUtils' + )); + $oControllerMock->method('getUtils')->willReturn($oUtilsMock); + + $this->_oController = $oControllerMock; + + $this->callMethod($this->_oController, 'render'); + } + + /** + * @test + * @throws ReflectionException + */ + public function renderDontRedirect() + { + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTUSER, 'foo'); + + /** @var Utils|PHPUnit_Framework_MockObject_MockObject $oUtilsMock */ + $oUtilsMock = $this->getMock(Utils::class, array( + 'redirect' + )); + $oUtilsMock->expects($this->never())->method('redirect')->willReturn(true); + + /** @var d3totplogin|PHPUnit_Framework_MockObject_MockObject $oControllerMock */ + $oControllerMock = $this->getMock(d3totplogin::class, array( + 'getUtils' + )); + $oControllerMock->method('getUtils')->willReturn($oUtilsMock); + + $this->_oController = $oControllerMock; + + $this->assertSame( + 'd3totplogin.tpl', + $this->callMethod($this->_oController, 'render') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getUtilsReturnsRightInstance() + { + $this->assertInstanceOf( + Utils::class, + $this->callMethod($this->_oController, 'getUtils') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getBackupCodeCountMessageReturnMessage() + { + /** @var d3backupcodelist|PHPUnit_Framework_MockObject_MockObject $oBackupCodesListMock */ + $oBackupCodesListMock = $this->getMock(d3backupcodelist::class, array( + 'getAvailableCodeCount' + )); + $oBackupCodesListMock->method('getAvailableCodeCount')->willReturn(1); + + /** @var d3totplogin|PHPUnit_Framework_MockObject_MockObject $oControllerMock */ + $oControllerMock = $this->getMock(d3totplogin::class, array( + 'getBackupCodeListObject' + )); + $oControllerMock->method('getBackupCodeListObject')->willReturn($oBackupCodesListMock); + + $this->_oController = $oControllerMock; + + $this->assertGreaterThan( + 0, + strpos( + $this->callMethod($this->_oController, 'getBackupCodeCountMessage'), + ' 1 ' + ) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getBackupCodeCountMessageReturnNoMessage() + { + /** @var d3backupcodelist|PHPUnit_Framework_MockObject_MockObject $oBackupCodesListMock */ + $oBackupCodesListMock = $this->getMock(d3backupcodelist::class, array( + 'getAvailableCodeCount' + )); + $oBackupCodesListMock->method('getAvailableCodeCount')->willReturn(1234); + + /** @var d3totplogin|PHPUnit_Framework_MockObject_MockObject $oControllerMock */ + $oControllerMock = $this->getMock(d3totplogin::class, array( + 'getBackupCodeListObject' + )); + $oControllerMock->method('getBackupCodeListObject')->willReturn($oBackupCodesListMock); + + $this->_oController = $oControllerMock; + + $this->assertEmpty( + $this->callMethod($this->_oController, 'getBackupCodeCountMessage') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getBackupCodeListObjectReturnsRightInstance() + { + $this->assertInstanceOf( + d3backupcodelist::class, + $this->callMethod($this->_oController, 'getBackupCodeListObject') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function canGetPreviousClass() + { + $className = "testClass"; + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, $className); + + $this->assertSame( + $className, + $this->callMethod($this->_oController, 'getPreviousClass') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function orderClassIsOrderStep() + { + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, 'order'); + + $this->assertTrue( + $this->callMethod( + $this->_oController, + 'previousClassIsOrderStep' + ) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function startClassIsNoOrderStep() + { + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, 'start'); + + $this->assertFalse( + $this->callMethod( + $this->_oController, + 'previousClassIsOrderStep' + ) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getIsOrderStepIsSameLikeOrderClass() + { + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, 'order'); + + $this->assertTrue( + $this->callMethod( + $this->_oController, + 'getIsOrderStep' + ) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getIsOrderStepIsSameLikeStartClass() + { + Registry::getSession()->setVariable(d3totp::TOTP_SESSION_CURRENTCLASS, 'start'); + + $this->assertFalse( + $this->callMethod( + $this->_oController, + 'getIsOrderStep' + ) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function canGetBreadCrumb() + { + $aBreadCrumb = $this->callMethod($this->_oController, 'getBreadCrumb'); + + $this->assertInternalType('string', $aBreadCrumb[0]['title']); + $this->assertTrue(strlen($aBreadCrumb[0]['title']) > 1); + $this->assertInternalType('string', $aBreadCrumb[0]['link']); + $this->assertTrue(strlen($aBreadCrumb[0]['link']) > 1); + } +} \ No newline at end of file