diff --git a/src/Application/Model/d3totp.php b/src/Application/Model/d3totp.php index e798da8..94576a5 100644 --- a/src/Application/Model/d3totp.php +++ b/src/Application/Model/d3totp.php @@ -16,12 +16,14 @@ namespace D3\Totp\Application\Model; use BaconQrCode\Renderer\Image\Svg; +use BaconQrCode\Renderer\RendererInterface; use BaconQrCode\Writer; use D3\ModCfg\Application\Model\d3database; use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException; use Doctrine\DBAL\DBALException; use OTPHP\TOTP; use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; use OxidEsales\Eshop\Core\DatabaseProvider; use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; use OxidEsales\Eshop\Core\Model\BaseModel; @@ -58,17 +60,27 @@ class d3totp extends BaseModel { $this->userId = $userId; $oQB = d3database::getInstance()->getQueryBuilder(); + $oDb = $this->d3GetDb(); - if (DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getOne("SHOW TABLES LIKE '".$this->tableName."'")) { + if ($oDb->getOne("SHOW TABLES LIKE '".$this->tableName."'")) { $oQB->select('oxid') ->from($this->getViewName()) ->where("oxuserid = " . $oQB->createNamedParameter($userId)) ->setMaxResults(1); - $this->load(DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getOne($oQB->getSQL(), $oQB->getParameters())); + $this->load($oDb->getOne($oQB->getSQL(), $oQB->getParameters())); } } + /** + * @return DatabaseInterface + * @throws DatabaseConnectionException + */ + public function d3GetDb() + { + return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC); + } + /** * @return User */ @@ -76,11 +88,19 @@ class d3totp extends BaseModel { $userId = $this->userId ? $this->userId : $this->getFieldData('oxuserid'); - $user = oxNew(User::class); + $user = $this->d3GetUser(); $user->load($userId); return $user; } + /** + * @return User + */ + public function d3GetUser() + { + return oxNew(User::class); + } + /** * @return bool */ @@ -156,11 +176,19 @@ class d3totp extends BaseModel $renderer->setHeight(200); $renderer->setWidth(200); - /** @var Writer $writer */ - $writer = oxNew(Writer::class, $renderer); + $writer = $this->d3GetWriter($renderer); return $writer->writeString($this->getTotp()->getProvisioningUri()); } + /** + * @param RendererInterface $renderer + * @return Writer + */ + public function d3GetWriter(RendererInterface $renderer) + { + return oxNew(Writer::class, $renderer); + } + /** * @return string */ @@ -192,8 +220,7 @@ class d3totp extends BaseModel { $blVerify = $this->getTotp($seed)->verify($totp, null, $this->timeWindow); if (false == $blVerify) { - /** @var d3backupcodelist $oBC */ - $oBC = oxNew(d3backupcodelist::class); + $oBC = $this->d3GetBackupCodeListObject(); $blVerify = $oBC->verify($totp); if (false == $blVerify) { @@ -205,6 +232,14 @@ class d3totp extends BaseModel return $blVerify; } + /** + * @return d3backupcodelist + */ + public function d3GetBackupCodeListObject() + { + return oxNew(d3backupcodelist::class); + } + /** * @param $plaintext * @return string @@ -216,7 +251,7 @@ class d3totp extends BaseModel $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); - return base64_encode($iv.$hmac.$ciphertext_raw); + return $this->d3Base64_decode($iv.$hmac.$ciphertext_raw); } /** @@ -226,7 +261,7 @@ class d3totp extends BaseModel public function decrypt($ciphertext) { $key = Registry::getConfig()->getConfigParam('sConfigKey'); - $c = base64_decode($ciphertext); + $c = $this->d3Base64_decode($ciphertext); $ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC"); $iv = substr($c, 0, $ivlen); $hmac = substr($c, $ivlen, $sha2len=32); @@ -240,6 +275,16 @@ class d3totp extends BaseModel return false; } + /** + * required for unit tests + * @param $source + * @return bool|string + */ + public function d3Base64_decode($source) + { + return base64_decode($source); + } + /** * @param null $oxid * @return bool @@ -247,7 +292,7 @@ class d3totp extends BaseModel */ public function delete($oxid = null) { - $oBackupCodeList = oxNew(d3backupcodelist::class); + $oBackupCodeList = $this->d3GetBackupCodeListObject(); $oBackupCodeList->deleteAllFromUser($this->getFieldData('oxuserid')); $blDelete = parent::delete(); diff --git a/src/tests/unit/Application/Model/d3backupcodeTest.php b/src/tests/unit/Application/Model/d3backupcodeTest.php index 6bf86f5..4d2fe2e 100644 --- a/src/tests/unit/Application/Model/d3backupcodeTest.php +++ b/src/tests/unit/Application/Model/d3backupcodeTest.php @@ -21,7 +21,6 @@ use D3\Totp\Application\Model\d3backupcode; use D3\Totp\Application\Model\d3totp; use D3\Totp\tests\unit\d3TotpUnitTestCase; use OxidEsales\Eshop\Application\Model\User; -use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Registry; use PHPUnit_Framework_MockObject_MockObject; use ReflectionException; diff --git a/src/tests/unit/Application/Model/d3backupcodelistTest.php b/src/tests/unit/Application/Model/d3backupcodelistTest.php index fc4642f..a5b7583 100644 --- a/src/tests/unit/Application/Model/d3backupcodelistTest.php +++ b/src/tests/unit/Application/Model/d3backupcodelistTest.php @@ -19,15 +19,11 @@ namespace D3\Totp\tests\unit\Application\Model; use D3\Totp\Application\Model\d3backupcode; use D3\Totp\Application\Model\d3backupcodelist; -use D3\Totp\Application\Model\d3totp; use D3\Totp\tests\unit\d3TotpUnitTestCase; use OxidEsales\Eshop\Application\Controller\FrontendController; use OxidEsales\Eshop\Application\Model\User; use OxidEsales\Eshop\Core\Config; -use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface; use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; -use OxidEsales\Eshop\Core\DatabaseProvider; -use OxidEsales\Eshop\Core\Registry; use PHPUnit_Framework_MockObject_MockObject; use ReflectionException; diff --git a/src/tests/unit/Application/Model/d3totpTest.php b/src/tests/unit/Application/Model/d3totpTest.php new file mode 100644 index 0000000..2d2be83 --- /dev/null +++ b/src/tests/unit/Application/Model/d3totpTest.php @@ -0,0 +1,819 @@ + + * @link http://www.oxidmodule.com + */ + +namespace D3\Totp\tests\unit\Application\Model; + +use BaconQrCode\Renderer\Image\Svg; +use BaconQrCode\Writer; +use D3\Totp\Application\Model\d3backupcode; +use D3\Totp\Application\Model\d3backupcodelist; +use D3\Totp\Application\Model\d3totp; +use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException; +use D3\Totp\tests\unit\d3TotpUnitTestCase; +use OTPHP\TOTP; +use OxidEsales\Eshop\Application\Controller\FrontendController; +use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database; +use OxidEsales\Eshop\Core\Registry; +use PHPUnit_Framework_MockObject_MockObject; +use ReflectionException; + +class d3totpTest extends d3TotpUnitTestCase +{ + /** @var d3totp */ + protected $_oModel; + + /** + * setup basic requirements + */ + public function setUp() + { + parent::setUp(); + + $this->_oModel = oxNew(d3totp::class); + } + + public function tearDown() + { + parent::tearDown(); + + unset($this->_oModel); + } + + /** + * @test + * @throws ReflectionException + */ + public function constructCallsInit() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'init', + )); + $oModelMock->expects($this->once())->method('init'); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, '__construct'); + } + + /** + * @test + * @throws ReflectionException + */ + public function loadByUserIdTableNotExist() + { + /** @var Database|PHPUnit_Framework_MockObject_MockObject $oDbMock */ + $oDbMock = $this->getMock(Database::class, array( + 'getOne' + ), array(), '', false); + $oDbMock->expects($this->once())->method('getOne')->willReturnOnConsecutiveCalls(false, true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetDb', + 'load' + )); + $oModelMock->method('d3GetDb')->willReturn($oDbMock); + $oModelMock->expects($this->never())->method('load')->willReturn(true); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'loadByUserId', ['foobar']); + } + + /** + * @test + * @throws ReflectionException + */ + public function loadByUserIdTableExist() + { + /** @var Database|PHPUnit_Framework_MockObject_MockObject $oDbMock */ + $oDbMock = $this->getMock(Database::class, array( + 'getOne' + ), array(), '', false); + $oDbMock->expects($this->exactly(2))->method('getOne')->willReturnOnConsecutiveCalls(true, true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetDb', + 'load' + )); + $oModelMock->method('d3GetDb')->willReturn($oDbMock); + $oModelMock->expects($this->once())->method('load')->willReturn(true); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'loadByUserId', ['foobar']); + } + + /** + * @test + * @throws ReflectionException + */ + public function getUserFromMember() + { + /** @var User|PHPUnit_Framework_MockObject_MockObject $oUserMock */ + $oUserMock = $this->getMock(User::class, array( + 'load' + )); + $oUserMock->method('load')->with('foobar')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetUser', + 'getFieldData', + )); + $oModelMock->method('d3GetUser')->willReturn($oUserMock); + $oModelMock->expects($this->never())->method('getFieldData')->willReturn(true); + + $this->_oModel = $oModelMock; + + $this->setValue($this->_oModel, 'userId', 'foobar'); + + $this->assertSame( + $oUserMock, + $this->callMethod($this->_oModel, 'getUser') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getUserFromObject() + { + $this->setValue($this->_oModel, 'userId', null); + + /** @var User|PHPUnit_Framework_MockObject_MockObject $oUserMock */ + $oUserMock = $this->getMock(User::class, array( + 'load' + )); + $oUserMock->method('load')->with('barfoo')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetUser', + 'getFieldData', + )); + $oModelMock->method('d3GetUser')->willReturn($oUserMock); + $oModelMock->expects($this->once())->method('getFieldData')->willReturn('barfoo'); + + $this->_oModel = $oModelMock; + + $this->assertSame( + $oUserMock, + $this->callMethod($this->_oModel, 'getUser') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function d3GetDbReturnsRightInstance() + { + $this->assertInstanceOf( + Database::class, + $this->callMethod($this->_oModel, 'd3GetDb') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function d3GetUserReturnsRightInstance() + { + $this->assertInstanceOf( + User::class, + $this->callMethod($this->_oModel, 'd3GetUser') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function isActivePass() + { + Registry::getConfig()->setConfigParam('blDisableTotpGlobally', false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'UserUseTotp', + )); + $oModelMock->method('UserUseTotp')->willReturn(true); + + $this->_oModel = $oModelMock; + + $this->assertTrue( + $this->callMethod($this->_oModel, 'isActive') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function isActiveFailedBecauseNoTotpUse() + { + Registry::getConfig()->setConfigParam('blDisableTotpGlobally', false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'UserUseTotp', + )); + $oModelMock->method('UserUseTotp')->willReturn(false); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'isActive') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function isActiveFailedBecauseConfigParam() + { + Registry::getConfig()->setConfigParam('blDisableTotpGlobally', true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'UserUseTotp', + )); + $oModelMock->method('UserUseTotp')->willReturn(true); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'isActive') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function isActiveFailedBecauseNoTotpUseAndConfigParam() + { + Registry::getConfig()->setConfigParam('blDisableTotpGlobally', true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'UserUseTotp', + )); + $oModelMock->method('UserUseTotp')->willReturn(false); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'isActive') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function UserUseTotpPass() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + )); + $oModelMock->method('getFieldData')->willReturnOnConsecutiveCalls(true, true); + + $this->_oModel = $oModelMock; + + $this->assertTrue( + $this->callMethod($this->_oModel, 'UserUseTotp') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function UserUseTotpNoTotp() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + )); + $oModelMock->method('getFieldData')->willReturnOnConsecutiveCalls(false, true); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'UserUseTotp') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function UserUseTotpNoSeed() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + )); + $oModelMock->method('getFieldData')->willReturnOnConsecutiveCalls(true, false); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'UserUseTotp') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function UserUseTotpNoTotpAndNoSeed() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + )); + $oModelMock->method('getFieldData')->willReturnOnConsecutiveCalls(false, false); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'UserUseTotp') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getSavedSecretExistingSeed() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + 'decrypt', + )); + $oModelMock->method('getFieldData')->willReturn('seed'); + $oModelMock->method('decrypt')->willReturn('unencseed'); + + $this->_oModel = $oModelMock; + + $this->assertSame( + 'unencseed', + $this->callMethod($this->_oModel, 'getSavedSecret') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getSavedSecretNoSeed() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + 'decrypt', + )); + $oModelMock->method('getFieldData')->willReturn(null); + $oModelMock->method('decrypt')->willReturn('unencseed'); + + $this->_oModel = $oModelMock; + + $this->assertNull( + $this->callMethod($this->_oModel, 'getSavedSecret') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getSavedSecretCantDecrypt() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getFieldData', + 'decrypt', + )); + $oModelMock->method('getFieldData')->willReturn('seed'); + $oModelMock->method('decrypt')->willReturn(false); + + $this->_oModel = $oModelMock; + + $this->assertNull( + $this->callMethod($this->_oModel, 'getSavedSecret') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getTotpReturnsCachedObject() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array(), array(), '', false); + + $this->setValue($this->_oModel, 'totp', $oTotpMock); + + $this->assertSame( + $oTotpMock, + $this->callMethod($this->_oModel, 'getTotp') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getTotpReturnsNewObject() + { + /** @var User|PHPUnit_Framework_MockObject_MockObject $oUserMock */ + $oUserMock = $this->getMock(User::class, array( + 'getFieldData', + )); + $oUserMock->method('getFieldData')->willReturn('username'); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getUser', + 'getSavedSecret' + )); + $oModelMock->method('getUser')->willReturn($oUserMock); + $oModelMock->method('getSavedSecret')->willReturn('savedSecret'); + + $this->_oModel = $oModelMock; + + /** @var TOTP $oTotp */ + $oTotp = $this->callMethod($this->_oModel, 'getTotp'); + + $this->assertInstanceOf(TOTP::class, $oTotp); + $this->assertSame('username', $oTotp->getLabel()); + $this->assertSame('SAVEDSECRET', $oTotp->getSecret()); + } + + /** + * @test + * @throws ReflectionException + */ + public function getTotpReturnsNewObjectNoUserGivenSeed() + { + /** @var User|PHPUnit_Framework_MockObject_MockObject $oUserMock */ + $oUserMock = $this->getMock(User::class, array( + 'getFieldData', + )); + $oUserMock->method('getFieldData')->willReturn(false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getUser', + 'getSavedSecret' + )); + $oModelMock->method('getUser')->willReturn($oUserMock); + $oModelMock->method('getSavedSecret')->willReturn('savedSecret'); + + $this->_oModel = $oModelMock; + + /** @var TOTP $oTotp */ + $oTotp = $this->callMethod($this->_oModel, 'getTotp', ['givenSeed']); + + $this->assertInstanceOf(TOTP::class, $oTotp); + $this->assertSame(null, $oTotp->getLabel()); + $this->assertSame('GIVENSEED', $oTotp->getSecret()); + } + + /** + * @test + * @throws ReflectionException + */ + public function getQrCodeUriPass() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'getQrCodeUri' + )); + $oTotpMock->expects($this->once())->method('getQrCodeUri')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getTotp' + )); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'getQrCodeUri'); + } + + /** + * @test + * @throws ReflectionException + */ + public function getQrCodeElement() + { + $renderer = oxNew(Svg::class); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'getProvisioningUri' + )); + $oTotpMock->method('getProvisioningUri')->willReturn(true); + + /** @var Writer|PHPUnit_Framework_MockObject_MockObject $oWriterMock */ + $oWriterMock = $this->getMock(Writer::class, array( + 'writeString' + ), array($renderer)); + $oWriterMock->expects($this->once())->method('writeString')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetWriter', + 'getTotp' + )); + $oModelMock->method('d3GetWriter')->willReturn($oWriterMock); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'getQrCodeElement'); + } + + /** + * @test + * @throws ReflectionException + */ + public function d3GetWriterReturnsRightInstance() + { + $renderer = oxNew(Svg::class); + + $this->assertInstanceOf( + Writer::class, + $this->callMethod($this->_oModel, 'd3GetWriter', [$renderer]) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function getSecretPass() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'getSecret' + )); + $oTotpMock->expects($this->once())->method('getSecret')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getTotp' + )); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'getSecret'); + } + + /** + * @test + * @throws ReflectionException + */ + public function saveSecretPass() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'encrypt' + )); + $oModelMock->method('encrypt')->willReturn('enc_secret'); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'saveSecret', ['newSecret']); + $this->assertSame( + 'enc_secret', + $this->callMethod($this->_oModel, 'getFieldData', ['seed']) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function verifyPass() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'verify' + )); + $oTotpMock->expects($this->once())->method('verify')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getTotp' + )); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + + $this->_oModel = $oModelMock; + + $this->assertTrue( + $this->callMethod($this->_oModel, 'verify', ['012345']) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function verifyBackupCodePass() + { + /** @var d3backupcodelist|PHPUnit_Framework_MockObject_MockObject $oBackupCodeListMock */ + $oBackupCodeListMock = $this->getMock(d3backupcodelist::class, array( + 'verify' + )); + $oBackupCodeListMock->expects($this->once())->method('verify')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'verify' + )); + $oTotpMock->expects($this->once())->method('verify')->willReturn(false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getTotp', + 'd3GetBackupCodeListObject' + )); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + $oModelMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock); + + $this->_oModel = $oModelMock; + + $this->assertTrue( + $this->callMethod($this->_oModel, 'verify', ['012345']) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function verifyFailed() + { + $this->setExpectedException(d3totp_wrongOtpException::class); + + /** @var d3backupcodelist|PHPUnit_Framework_MockObject_MockObject $oBackupCodeListMock */ + $oBackupCodeListMock = $this->getMock(d3backupcodelist::class, array( + 'verify' + )); + $oBackupCodeListMock->expects($this->once())->method('verify')->willReturn(false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oTotpMock */ + $oTotpMock = $this->getMock(d3totp::class, array( + 'verify' + )); + $oTotpMock->expects($this->once())->method('verify')->willReturn(false); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'getTotp', + 'd3GetBackupCodeListObject' + )); + $oModelMock->method('getTotp')->willReturn($oTotpMock); + $oModelMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock); + + $this->_oModel = $oModelMock; + + $this->callMethod($this->_oModel, 'verify', ['012345']); + } + + /** + * @test + * @throws ReflectionException + */ + public function d3GetBackupCodeListObjectReturnsRightInstance() + { + $this->assertInstanceOf( + d3backupcodelist::class, + $this->callMethod($this->_oModel, 'd3GetBackupCodeListObject') + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function encryptDecryptPass() + { + $sReturn = $this->callMethod($this->_oModel, 'encrypt', ['foobar']); + + $this->assertInternalType('string', $sReturn); + $this->assertNotSame('foobar', $sReturn); + } + + /** + * @test + * @throws ReflectionException + */ + public function decryptPass() + { + $sReturn = $this->callMethod( + $this->_oModel, + 'decrypt', + ['L5cSqld/1jpoSHnbxF1/+lGqN8OM7FWt2CagEkqNeRMvkyogrl0msvSuOpLwDwngvSa80bfDnfwWrPe5c6pdww=='] + ); + + $this->assertSame('foobar', $sReturn); + } + + /** + * @test + * @throws ReflectionException + */ + public function decryptFailed() + { + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3Base64_decode', + )); + $oModelMock->method('d3Base64_decode')->willReturn( + str_pad('foobar', 16, 0, STR_PAD_LEFT) + ); + + $this->_oModel = $oModelMock; + + $sReturn = $this->callMethod( + $this->_oModel, + 'decrypt', + ['L5cSqld/1jpoSHnbxF1/+lGqN8OM7FWt2CagEkqNeRMvkyogrl0msvSuOpLwDwngvSa80bfDnfwWrPe5c6pdww=='] + ); + + $this->assertFalse($sReturn); + } + + /** + * @test + * @throws ReflectionException + */ + public function d3Base64_decodePass() + { + $this->assertSame( + 'foobar', + $this->callMethod($this->_oModel, 'd3Base64_decode', [base64_encode('foobar')]) + ); + } + + /** + * @test + * @throws ReflectionException + */ + public function deletePass() + { + /** @var d3backupcodelist|PHPUnit_Framework_MockObject_MockObject $oBackupCodeListMock */ + $oBackupCodeListMock = $this->getMock(d3backupcodelist::class, array( + 'deleteAllFromUser' + )); + $oBackupCodeListMock->expects($this->once())->method('deleteAllFromUser')->willReturn(true); + + /** @var d3totp|PHPUnit_Framework_MockObject_MockObject $oModelMock */ + $oModelMock = $this->getMock(d3totp::class, array( + 'd3GetBackupCodeListObject', + 'getFieldData', + 'canDelete' + )); + $oModelMock->method('d3GetBackupCodeListObject')->willReturn($oBackupCodeListMock); + $oModelMock->method('getFieldData')->willReturn('newId'); + $oModelMock->method('canDelete')->willReturn(false); + + $this->_oModel = $oModelMock; + + $this->assertFalse( + $this->callMethod($this->_oModel, 'delete') + ); + } +} \ No newline at end of file