move backup codes to separate db table, clear backup code if used or new codes are generated

This commit is contained in:
Daniel Seifert 2019-07-28 23:00:30 +02:00
parent 2e9fca06c0
commit a42121b4bc
10 changed files with 348 additions and 163 deletions

View File

@ -1,19 +1,23 @@
CREATE TABLE `d3totp` (
`OXID` CHAR(32) NOT NULL,
`OXUSERID` CHAR(32) NOT NULL,
`USETOTP` TINYINT(1) NOT NULL DEFAULT '0',
`SEED` VARCHAR(256) NOT NULL DEFAULT '0',
`BC1` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #1',
`BC2` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #2',
`BC3` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #3',
`BC4` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #4',
`BC5` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #5',
`BC6` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #6',
`BC7` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #7',
`BC8` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #8',
`BC9` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #9',
`BC10` VARCHAR(64) NOT NULL DEFAULT '0' COMMENT 'BackupCode #10',
PRIMARY KEY (`OXID`),
UNIQUE INDEX `OXUSERID` (`OXUSERID`)
)
CREATE TABLE IF NOT EXISTS `d3totp` (
`OXID` CHAR(32) NOT NULL ,
`OXUSERID` CHAR(32) NOT NULL ,
`USETOTP` TINYINT(1) NOT NULL DEFAULT 0,
`SEED` VARCHAR(256) NOT NULL ,
`OXTIMESTAMP` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
PRIMARY KEY (`OXID`) ,
UNIQUE KEY `OXUSERID` (`OXUSERID`)
)
ENGINE=InnoDB
COMMENT='totp setting';
CREATE TABLE IF NOT EXISTS `d3totp_backupcodes` (
`OXID` CHAR(32) NOT NULL ,
`OXUSERID` CHAR(32) NOT NULL COMMENT 'user id',
`BACKUPCODE` VARCHAR(64) NOT NULL COMMENT 'BackupCode',
`OXTIMESTAMP` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
PRIMARY KEY (`OXID`) ,
KEY `OXUSERID` (`OXUSERID`) ,
KEY `BACKUPCODE` (`BACKUPCODE`)
)
ENGINE=InnoDB
COMMENT='totp backup codes';

View File

@ -1 +1 @@
# no update instructions available
# no update instructions available

View File

@ -16,11 +16,11 @@
namespace D3\Totp\Application\Controller\Admin;
use D3\Totp\Application\Model\d3totp;
use D3\Totp\Application\Model\Exceptions\d3backupcodelist;
use D3\Totp\Modules\Application\Model\d3_totp_user;
use Doctrine\DBAL\DBALException;
use Exception;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
@ -30,10 +30,10 @@ class d3user_totp extends AdminDetailsController
protected $_sThisTemplate = 'd3user_totp.tpl';
public $aBackupCodes = array();
/**
* @return string
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function render()
{
@ -60,7 +60,7 @@ class d3user_totp extends AdminDetailsController
}
/**
* @throws \Exception
* @throws Exception
*/
public function save()
{
@ -74,6 +74,7 @@ class d3user_totp extends AdminDetailsController
/** @var d3_totp_user $oUser */
$oUser = oxNew(User::class);
$oUser->load($this->getEditObjectId());
if (false == $oUser->isSamePassword($pwd)) {
$oException = oxNew(StandardException::class, 'D3_TOTP_ERROR_PWDONTPASS');
throw $oException;
@ -81,6 +82,7 @@ class d3user_totp extends AdminDetailsController
/** @var d3totp $oTotp */
$oTotp = oxNew(d3totp::class);
$oTotpBackupCodes = oxNew(d3backupcodelist::class);
if ($aParams['d3totp__oxid']) {
$oTotp->load($aParams['d3totp__oxid']);
} else {
@ -91,11 +93,12 @@ class d3user_totp extends AdminDetailsController
$oTotp->saveSecret($seed);
$oTotp->assign($aParams);
$oTotp->verify($otp, $seed);
$this->addTplParam('aBackupCodes', $oTotp->generateBackupCodes());
$oTotpBackupCodes->generateBackupCodes($this->getEditObjectId());
$oTotp->setId();
}
$oTotp->save();
} catch (\Exception $oExcp) {
$oTotpBackupCodes->save();
} catch (Exception $oExcp) {
$this->_sSaveError = $oExcp->getMessage();
}
}
@ -111,4 +114,20 @@ class d3user_totp extends AdminDetailsController
$oTotp->delete();
}
}
/**
* @param $aCodes
*/
public function setBackupCodes($aCodes)
{
$this->aBackupCodes = $aCodes;
}
/**
* @return string
*/
public function getBackupCodes()
{
return implode(PHP_EOL, $this->aBackupCodes);
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* This Software is the property of Data Development and is protected
* by copyright law - it is NOT Freeware.
* Any unauthorized use of this software without a valid license
* is a violation of the license agreement and will be prosecuted by
* civil and criminal law.
* http://www.shopmodule.com
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link http://www.oxidmodule.com
*/
namespace D3\Totp\Application\Model\Exceptions;
use D3\Totp\Application\Controller\Admin\d3user_totp;
use D3\Totp\Application\Model\d3backupcode;
use Exception;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\ListModel;
use OxidEsales\Eshop\Core\Registry;
class d3backupcodelist extends ListModel
{
protected $_sObjectsInListName = d3backupcode::class;
/**
* Core table name
*
* @var string
*/
protected $_sCoreTable = 'd3totp_backupcodes';
protected $_backupCodes = array();
/**
* @param $sUserId
* @throws DatabaseConnectionException
*/
public function generateBackupCodes($sUserId)
{
$this->deleteAllFromUser($sUserId);
for ($i = 1; $i <= 10; $i++) {
$oBackupCode = oxNew(d3backupcode::class);
$this->_backupCodes[] = $oBackupCode->generateCode($sUserId);
$this->offsetSet(md5(rand()), $oBackupCode);
}
/** @var d3user_totp $oActView */
$oActView = Registry::getConfig()->getActiveView();
$oActView->setBackupCodes($this->_backupCodes);
}
/**
* @throws Exception
*/
public function save()
{
/** @var d3backupcode $oBackupCode */
foreach ($this->getArray() as $oBackupCode) {
$oBackupCode->save();
}
}
/**
* @return d3backupcode
*/
public function getBaseObject()
{
/** @var d3backupcode $object */
$object = parent::getBaseObject();
return $object;
}
/**
* @param $totp
* @return bool
* @throws DatabaseConnectionException
*/
public function verify($totp)
{
$oDb = DatabaseProvider::getDb();
$query = "SELECT oxid FROM ".$this->getBaseObject()->getViewName().
" WHERE ".$oDb->quoteIdentifier('backupcode')." = ".$oDb->quote($this->getBaseObject()->d3EncodeBC($totp))." AND ".
$oDb->quoteIdentifier("oxuserid") ." = ".$oDb->quote($this->getUser()->getId());
$sVerify = $oDb->getOne($query);
$this->getBaseObject()->delete($sVerify);
return (bool) $sVerify;
}
/**
* @param $sUserId
* @throws DatabaseConnectionException
*/
public function deleteAllFromUser($sUserId)
{
$oDb = DatabaseProvider::getDb();
$query = "SELECT OXID FROM ".$oDb->quoteIdentifier($this->getBaseObject()->getCoreTableName()).
" WHERE ".$oDb->quoteIdentifier('oxuserid')." = ".$oDb->quote($sUserId);
$this->selectString($query);
/** @var d3backupcode $oBackupCode */
foreach ($this->getArray() as $oBackupCode) {
$oBackupCode->delete();
}
}
}

View File

@ -17,6 +17,7 @@
namespace D3\Totp\Application\Model\Exceptions;
use Exception;
use OxidEsales\Eshop\Core\Exception\StandardException;
class d3totp_wrongOtpException extends StandardException
@ -26,9 +27,9 @@ class d3totp_wrongOtpException extends StandardException
*
* @param string $sMessage exception message
* @param integer $iCode exception code
* @param \Exception|null $previous previous exception
* @param Exception|null $previous previous exception
*/
public function __construct($sMessage = "D3_TOTP_ERROR_UNVALID", $iCode = 0, \Exception $previous = null)
public function __construct($sMessage = "D3_TOTP_ERROR_UNVALID", $iCode = 0, Exception $previous = null)
{
parent::__construct($sMessage, $iCode, $previous);
}

View File

@ -0,0 +1,62 @@
<?php
/**
* This Software is the property of Data Development and is protected
* by copyright law - it is NOT Freeware.
* Any unauthorized use of this software without a valid license
* is a violation of the license agreement and will be prosecuted by
* civil and criminal law.
* http://www.shopmodule.com
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link http://www.oxidmodule.com
*/
namespace D3\Totp\Application\Model;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use RandomLib\Factory;
use RandomLib\Generator;
class d3backupcode extends BaseModel
{
protected $_sCoreTable = 'd3totp_backupcodes';
/**
* @param $sUserId
* @return string
* @throws DatabaseConnectionException
*/
public function generateCode($sUserId)
{
$factory = new Factory();
$generator = $factory->getLowStrengthGenerator();
$sCode = $generator->generateString(6, Generator::CHAR_DIGITS);
$this->assign(
array(
'oxuserid' => $sUserId,
'backupcode' => $this->d3EncodeBC($sCode),
)
);
return $sCode;
}
/**
* @param $code
* @return false|string
* @throws DatabaseConnectionException
*/
public function d3EncodeBC($code)
{
$oDb = DatabaseProvider::getDb();
$salt = $this->getUser()->getFieldData('oxpasssalt');
$sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )";
return $oDb->getOne($sSelect);
}
}

View File

@ -18,6 +18,7 @@ namespace D3\Totp\Application\Model;
use BaconQrCode\Renderer\Image\Svg;
use BaconQrCode\Writer;
use D3\ModCfg\Application\Model\d3database;
use D3\Totp\Application\Model\Exceptions\d3backupcodelist;
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
use Doctrine\DBAL\DBALException;
use OTPHP\TOTP;
@ -26,8 +27,6 @@ use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
use RandomLib\Factory;
use RandomLib\Generator;
class d3totp extends BaseModel
{
@ -37,7 +36,6 @@ class d3totp extends BaseModel
public $userId;
public $totp;
protected $timeWindow = 2;
protected $_backupCodes = array();
/**
* d3totp constructor.
@ -119,28 +117,6 @@ class d3totp extends BaseModel
return null;
}
/**
* @return array
* @throws DatabaseConnectionException
*/
public function generateBackupCodes()
{
$factory = new Factory();
$generator = $factory->getLowStrengthGenerator();
for ($i = 1; $i <= 10; $i++) {
$sCode = $generator->generateString(6, Generator::CHAR_DIGITS);
$this->_backupCodes[] = $sCode;
$this->assign(
array(
'bc'.$i => $this->d3EncodeBC($sCode)
)
);
}
return $this->_backupCodes;
}
/**
* @param $seed
* @return TOTP
@ -206,20 +182,6 @@ class d3totp extends BaseModel
);
}
/**
* @param $code
* @return false|string
* @throws DatabaseConnectionException
*/
public function d3EncodeBC($code)
{
$oDb = DatabaseProvider::getDb();
$salt = $this->getUser()->getFieldData('oxpasssalt');
$sSelect = "SELECT BINARY MD5( CONCAT( " . $oDb->quote($code) . ", UNHEX( ".$oDb->quote($salt)." ) ) )";
return $oDb->getOne($sSelect);
}
/**
* @param $totp
* @param $seed
@ -231,14 +193,9 @@ class d3totp extends BaseModel
{
$blVerify = $this->getTotp($seed)->verify($totp, null, $this->timeWindow);
if (false == $blVerify) {
$oDb = DatabaseProvider::getDb();
$aFields = array('bc1', 'bc2', 'bc3', 'bc4', 'bc5', 'bc6', 'bc7', 'bc8', 'bc9', 'bc10');
$query = "SELECT 1 FROM ".$this->getViewName().
" WHERE ".$oDb->quote($this->d3EncodeBC($totp))." IN (".implode(', ', array_map([$oDb, 'quoteIdentifier'], $aFields)).") AND ".
$oDb->quoteIdentifier("oxuserid") ." = ".$oDb->quote($this->getUser()->getId());
$blVerify = (bool) $oDb->getOne($query);
/** @var d3backupcodelist $oBC */
$oBC = oxNew(d3backupcodelist::class);
$blVerify = $oBC->verify($totp);
if (false == $blVerify) {
$oException = oxNew(d3totp_wrongOtpException::class);
@ -283,4 +240,19 @@ class d3totp extends BaseModel
return false;
}
/**
* @param null $oxid
* @return bool
* @throws DatabaseConnectionException
*/
public function delete($oxid = null)
{
$oBackupCodeList = oxNew(d3backupcodelist::class);
$oBackupCodeList->deleteAllFromUser($this->getFieldData('oxuserid'));
$blDelete = parent::delete();
return $blDelete;
}
}

View File

@ -41,7 +41,7 @@ $aLang = [
'D3_TOTP_REGISTERDELETE_DESC' => 'Um die Registrierung zu ändern, löschen Sie diese bitte vorerst. Sie können sofort im Anschluss eine neue Registrierung anlegen.<br>Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zweifaktorauthentisierung geschützt.',
'D3_TOTP_BACKUPCODES' => 'Backup-Codes',
'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backup-Codes 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-Autetizierung aä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_BACKUPCODES_DESC' => 'Mit diesen Backup-Codes 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_SAVE' => 'Speichern',

View File

@ -29,7 +29,7 @@
<input type="hidden" name="editval[d3totp__oxuserid]" value="[{$oxid}]">
[{if $sSaveError}]
<table cellspacing="0" cellpadding="0" border="0" style="width:98%;">
<table style="padding:0; border:0; width:98%;">
<tr>
<td></td>
<td class="errorbox">[{oxmultilang ident=$sSaveError}]</td>
@ -38,10 +38,10 @@
[{/if}]
[{if $oxid && $oxid != '-1'}]
<table cellspacing="0" cellpadding="0" border="0" style="width:98%;">
<table style="padding:0; border:0; width:98%;">
<tr>
<td valign="top" class="edittext" style="padding-top:10px;padding-left:10px; width: 50%;">
<table cellspacing="0" cellpadding="0" border="0">
<td class="edittext" style="vertical-align: top; padding-top:10px;padding-left:10px; width: 50%;">
<table style="padding:0; border:0">
[{block name="user_d3user_totp_form1"}]
[{if false == $totp->getId()}]
<tr>
@ -80,8 +80,8 @@
</table>
</td>
<!-- Anfang rechte Seite -->
<td valign="top" class="edittext" align="left" style="height:99%;padding-left:5px;padding-bottom:30px;padding-top:10px; width: 50%;">
<table cellspacing="0" cellpadding="0" border="0">
<td class="edittext" style="text-align: left; vertical-align: top; height:99%;padding-left:5px;padding-bottom:30px;padding-top:10px; width: 50%;">
<table style="padding:0; border:0">
[{block name="user_d3user_totp_form2"}]
[{if false == $totp->getId()}]
<tr>
@ -124,7 +124,7 @@
</td>
</tr>
[{else}]
[{if $aBackupCodes}]
[{if $oView->getBackupCodes()}]
<tr>
<td class="edittext" colspan="2">
@ -136,10 +136,7 @@
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
<br>
<br>
<textarea id="backupcodes" rows="10" cols="20">[{strip}]
[{'
'|implode:$aBackupCodes}]
[{/strip}]</textarea>
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
</td>
</tr>
[{/if}]

View File

@ -27,6 +27,8 @@ 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',
@ -76,103 +78,54 @@ class Installation extends d3install_updatebase
'sExtra' => '',
'blMultilang' => false,
),
'BC1' => array(
'OXTIMESTAMP' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC1',
'sType' => 'VARCHAR(64)',
'sFieldName' => 'OXTIMESTAMP',
'sType' => 'TIMESTAMP',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #1',
'sDefault' => 'CURRENT_TIMESTAMP',
'sComment' => 'Timestamp',
'sExtra' => '',
'blMultilang' => false,
),
'BC2' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC2',
'sType' => 'VARCHAR(64)',
'bc_OXID' => array(
'sTableName' => 'd3totp_backupcodes',
'sFieldName' => 'OXID',
'sType' => 'CHAR(32)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #2',
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'BC3' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC3',
'sType' => 'VARCHAR(64)',
'bc_OXUSERID' => array(
'sTableName' => 'd3totp_backupcodes',
'sFieldName' => 'OXUSERID',
'sType' => 'CHAR(32)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #3',
'sComment' => 'user id',
'sExtra' => '',
'blMultilang' => false,
),
'BC4' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC4',
'bc_BACKUPCODE' => array(
'sTableName' => 'd3totp_backupcodes',
'sFieldName' => 'BACKUPCODE',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #4',
'sComment' => 'BackupCode',
'sExtra' => '',
'blMultilang' => false,
),
'BC5' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC5',
'sType' => 'VARCHAR(64)',
'bc_OXTIMESTAMP' => array(
'sTableName' => 'd3totp_backupcodes',
'sFieldName' => 'OXTIMESTAMP',
'sType' => 'TIMESTAMP',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #5',
'sExtra' => '',
'blMultilang' => false,
),
'BC6' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC6',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #6',
'sExtra' => '',
'blMultilang' => false,
),
'BC7' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC7',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #7',
'sExtra' => '',
'blMultilang' => false,
),
'BC8' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC8',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #8',
'sExtra' => '',
'blMultilang' => false,
),
'BC9' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC9',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #9',
'sExtra' => '',
'blMultilang' => false,
),
'BC10' => array(
'sTableName' => 'd3totp',
'sFieldName' => 'BC10',
'sType' => 'VARCHAR(64)',
'blNull' => false,
'sDefault' => false,
'sComment' => 'BackupCode #10',
'sDefault' => 'CURRENT_TIMESTAMP',
'sComment' => 'Timestamp',
'sExtra' => '',
'blMultilang' => false,
)
@ -194,7 +147,31 @@ class Installation extends d3install_updatebase
'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');
@ -233,4 +210,39 @@ class Installation extends d3install_updatebase
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;
}
}