geforked van D3Public/oxtotp
integrate backend, save encrypted seed only
This commit is contained in:
bovenliggende
fd3dbf5a12
commit
2f196aaef7
@ -16,12 +16,18 @@
|
||||
namespace D3\Totp\Application\Controller\Admin;
|
||||
|
||||
use D3\Totp\Application\Model\d3totp;
|
||||
use D3\Totp\Modules\Application\Model\d3_totp_user;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
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;
|
||||
|
||||
class d3user_totp extends AdminDetailsController
|
||||
{
|
||||
protected $_sSaveError = null;
|
||||
|
||||
protected $_sThisTemplate = 'd3user_totp.tpl';
|
||||
|
||||
/**
|
||||
@ -33,18 +39,62 @@ class d3user_totp extends AdminDetailsController
|
||||
{
|
||||
parent::render();
|
||||
|
||||
$soxId = $this->_aViewData["oxid"] = $this->getEditObjectId();
|
||||
$soxId = $this->getEditObjectId();
|
||||
|
||||
if (isset($soxId) && $soxId != "-1") {
|
||||
/** @var d3totp $oTotp */
|
||||
$oTotp = oxNew(d3totp::class);
|
||||
$oTotp->loadByUserId($soxId);
|
||||
$this->_aViewData["edit"] = $oTotp;
|
||||
/** @var d3_totp_user $oUser */
|
||||
$oUser = oxNew(User::class);
|
||||
if ($oUser->load($soxId)) {
|
||||
$this->addTplParam("oxid", $oUser->getId());
|
||||
} else {
|
||||
$this->addTplParam("oxid", '-1');
|
||||
}
|
||||
$this->addTplParam("edit", $oUser);
|
||||
}
|
||||
|
||||
if (!$this->_allowAdminEdit($soxId)) {
|
||||
$this->_aViewData['readonly'] = true;
|
||||
if ($this->_sSaveError) {
|
||||
$this->addTplParam("sSaveError", $this->_sSaveError);
|
||||
}
|
||||
|
||||
return $this->_sThisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
parent::save();
|
||||
|
||||
$aParams = Registry::getRequest()->getRequestEscapedParameter("editval");
|
||||
|
||||
try {
|
||||
$pwd = Registry::getRequest()->getRequestEscapedParameter("pwd");
|
||||
|
||||
/** @var d3_totp_user $oUser */
|
||||
$oUser = oxNew(User::class);
|
||||
if (false == $oUser->d3CheckPasswordPass($this->getEditObjectId(), $pwd)) {
|
||||
$oException = oxNew(StandardException::class, 'EXCEPTION_USER_PASSWORDDONTPASS');
|
||||
throw $oException;
|
||||
}
|
||||
|
||||
/** @var d3totp $oTotp */
|
||||
$oTotp = oxNew(d3totp::class);
|
||||
if ($aParams['d3totp__oxid']) {
|
||||
$oTotp->load($aParams['d3totp__oxid']);
|
||||
} else {
|
||||
$aParams['d3totp__usetotp'] = 1;
|
||||
$seed = Registry::getRequest()->getRequestEscapedParameter("secret");
|
||||
$otp = Registry::getRequest()->getRequestEscapedParameter("otp");
|
||||
|
||||
$oTotp->saveSecret($seed, $pwd);
|
||||
$oTotp->assign($aParams);
|
||||
$oTotp->verify($otp, $seed);
|
||||
$oTotp->setId();
|
||||
}
|
||||
$oTotp->save();
|
||||
} catch (\Exception $oExcp) {
|
||||
$this->_sSaveError = $oExcp->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
@ -81,7 +81,8 @@ class d3totp extends BaseModel
|
||||
*/
|
||||
public function UserUseTotp()
|
||||
{
|
||||
return $this->getFieldData('usetotp');
|
||||
return $this->getFieldData('usetotp')
|
||||
&& $this->getFieldData('seed');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,20 +91,24 @@ class d3totp extends BaseModel
|
||||
*/
|
||||
public function getSavedSecret()
|
||||
{
|
||||
$secret = $this->getFieldData('seed');
|
||||
$seed_enc = $this->getFieldData('seed');
|
||||
$sPwd = Registry::getSession()->getVariable('pwdTransmit');
|
||||
|
||||
if ($secret) {
|
||||
return $secret;
|
||||
if ($seed_enc && $sPwd) {
|
||||
$seed = $this->decrypt($seed_enc, $sPwd);
|
||||
if ($seed) {
|
||||
return $seed;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $seed
|
||||
* @return TOTP
|
||||
*/
|
||||
public function getTotp()
|
||||
public function getTotp($seed = null)
|
||||
{
|
||||
if (false == $this->totp) {
|
||||
|
||||
@ -113,10 +118,12 @@ class d3totp extends BaseModel
|
||||
$this->getUser()->getFieldData('oxusername')
|
||||
? $this->getUser()->getFieldData('oxusername')
|
||||
: null,
|
||||
$this->getSavedSecret()
|
||||
$seed
|
||||
? $seed
|
||||
: $this->getSavedSecret()
|
||||
);
|
||||
} else { // version 0.9 (>= PHP 7.1)
|
||||
$this->totp = TOTP::create($this->getSavedSecret());
|
||||
$this->totp = TOTP::create($seed ? $seed : $this->getSavedSecret());
|
||||
$this->totp->setLabel($this->getUser()->getFieldData('oxusername')
|
||||
? $this->getUser()->getFieldData('oxusername')
|
||||
: null
|
||||
@ -151,17 +158,31 @@ class d3totp extends BaseModel
|
||||
*/
|
||||
public function getSecret()
|
||||
{
|
||||
return $this->getTotp()->getSecret();
|
||||
return trim($this->getTotp()->getSecret());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $seed
|
||||
* @param $key
|
||||
*/
|
||||
public function saveSecret($seed, $key)
|
||||
{
|
||||
$this->assign(
|
||||
array(
|
||||
'seed' => $this->encrypt($seed, $key)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $totp
|
||||
* @param $seed
|
||||
* @return string
|
||||
* @throws d3totp_wrongOtpException
|
||||
*/
|
||||
public function verify($totp)
|
||||
public function verify($totp, $seed = null)
|
||||
{
|
||||
$blVerify = $this->getTotp()->verify($totp, null, 2);
|
||||
$blVerify = $this->getTotp($seed)->verify($totp, null, 2);
|
||||
if (false == $blVerify) {
|
||||
$oException = oxNew(d3totp_wrongOtpException::class, 'unvalid TOTP');
|
||||
throw $oException;
|
||||
@ -169,4 +190,43 @@ class d3totp extends BaseModel
|
||||
|
||||
return $blVerify;
|
||||
}
|
||||
|
||||
/**
|
||||
* $key should have previously been generated in a cryptographically secure manner, e.g. via openssl_random_pseudo_bytes
|
||||
*
|
||||
* @param $plaintext
|
||||
* @param $key
|
||||
* @return string
|
||||
*/
|
||||
public function encrypt($plaintext, $key)
|
||||
{
|
||||
$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);
|
||||
return base64_encode($iv.$hmac.$ciphertext_raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* $key should have previously been generated in a cryptographically secure manner, e.g. via openssl_random_pseudo_bytes
|
||||
*
|
||||
* @param $ciphertext
|
||||
* @param $key
|
||||
* @return bool|string
|
||||
*/
|
||||
public function decrypt($ciphertext, $key)
|
||||
{
|
||||
$c = base64_decode($ciphertext);
|
||||
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
|
||||
$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);
|
||||
if (hash_equals($hmac, $calcmac)) { // PHP 5.6+ compute attack-safe comparison
|
||||
return $original_plaintext;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@
|
||||
<input type="text" name="d3totp" id="d3totp" value="" size="49" autofocus autocomplete="off"><br>
|
||||
|
||||
[{oxmultilang ident="TOTP_INPUT_HELP"}]
|
||||
|
||||
--Anmeldung abbrechen--
|
||||
[{else}]
|
||||
[{$smarty.block.parent}]
|
||||
[{/if}]
|
||||
[{/if}]
|
@ -25,9 +25,13 @@ $aLang = [
|
||||
|
||||
'd3mxuser_totp' => '2-Faktor-Authentisierung',
|
||||
|
||||
'D3_TOTP_ACTIVE' => 'Benutzer verwendet 2-Faktor-Authentisierung',
|
||||
'D3_TOTP_ACTIVE_HELP' => 'Benutzer verwendet 2-Faktor-Authentisierung',
|
||||
'D3_TOTP_REGISTERNEW' => 'neue Registrierung erstellen',
|
||||
'D3_TOTP_QRCODE' => 'QR-Code',
|
||||
'D3_TOTP_QRCODE_HELP' => 'Scannen Sie diesen QR-Code mit Ihrer Authentisierungs-App, um dieses Benutzerkonto dort zu hinterlegen.',
|
||||
'D3_TOTP_SECRET' => 'QR-Code kann nicht gescannt werden?',
|
||||
'D3_TOTP_SECRET_HELP' => 'Setzen Sie keine App ein, die den QR-Code scannen kann, können Sie diese Zeichenkette auch in Ihr Authentisierungstool kopieren. Stellen Sie bitte zusätzlich die Passwortlänge auf 6 Zeichen und das Zeitinterval auf 30 Sekunden ein.',
|
||||
'D3_TOTP_CURRPWD' => 'Anmeldepasswort des Benutzerkontos',
|
||||
'D3_TOTP_CURRPWD_HELP' => 'Die Zeichenkette wird verschlüsselt im Shop abgelegt. Zum Verschlüsseln wird das Passwort des ausgewählten Kundenkontos benötigt. Zugleich stellt dies sicher, dass nur Berechtigte Änderungen an diesen Einstellungen vornehmen dürfen.',
|
||||
'D3_TOTP_CURROTP' => 'Bestätigung mit Einmalpasswort',
|
||||
'D3_TOTP_CURROTP_HELP' => 'Haben Sie dieses Kundenkonto in Ihrer Authentisierungs-App registriert, generieren Sie damit ein Einmalpasswort, tragen Sie es hier ein und senden das Formular direkt darauf hin ab.',
|
||||
];
|
||||
|
@ -1,5 +1,7 @@
|
||||
[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign}]
|
||||
|
||||
[{assign var="totp" value=$edit->d3GetTotp()}]
|
||||
|
||||
[{if $readonly}]
|
||||
[{assign var="readonly" value="readonly disabled"}]
|
||||
[{else}]
|
||||
@ -17,80 +19,98 @@
|
||||
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
|
||||
<input type="hidden" name="fnc" value="">
|
||||
<input type="hidden" name="oxid" value="[{$oxid}]">
|
||||
<input type="hidden" name="editval[d3totp__oxid]" value="[{$totp->getId()}]">
|
||||
<input type="hidden" name="editval[d3totp__oxuserid]" value="[{$oxid}]">
|
||||
<table cellspacing="0" cellpadding="0" border="0" style="width:98%;">
|
||||
<tr>
|
||||
<td valign="top" class="edittext" style="padding-top:10px;padding-left:10px; width: 50%;">
|
||||
<table cellspacing="0" cellpadding="0" border="0">
|
||||
[{block name="user_d3user_totp_form1"}]
|
||||
<tr>
|
||||
<td class="edittext" width="120">
|
||||
[{oxmultilang ident="D3_TOTP_ACTIVE"}]
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<input type="hidden" name="editval[d3totp__usetoptp]" value="0">
|
||||
<input class="edittext" type="checkbox" name="editval[d3totp__usetotp]" value='1' [{if $edit->getFieldData('usetotp') == 1}]checked[{/if}] [{$readonly}]>
|
||||
[{oxinputhelp ident="D3_TOTP_ACTIVE_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
[{/block}]
|
||||
[{if $sSaveError}]
|
||||
<table cellspacing="0" cellpadding="0" border="0" style="width:98%;">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="errorbox">[{oxmultilang ident=$sSaveError}]</td>
|
||||
</tr>
|
||||
</table>
|
||||
[{/if}]
|
||||
|
||||
<tr>
|
||||
<td class="edittext" colspan="2"><br><br>
|
||||
<input type="submit" class="edittext" id="oLockButton" name="saveArticle" value="[{oxmultilang ident="ARTICLE_MAIN_SAVE"}]" onClick="Javascript:document.myedit.fnc.value='save'" [{if !$edit->oxarticles__oxtitle->value && !$oxparentid}]disabled[{/if}] [{$readonly}]>
|
||||
[{if $oxid!=-1 && !$readonly}]
|
||||
<input type="submit" class="edittext" name="save" value="[{oxmultilang ident="ARTICLE_MAIN_ARTCOPY"}]" onClick="Javascript:document.myedit.fnc.value='copyArticle';" [{$readonly}]>
|
||||
[{if $oxid && $oxid != '-1'}]
|
||||
<table cellspacing="0" cellpadding="0" border="0" style="width:98%;">
|
||||
<tr>
|
||||
<td valign="top" class="edittext" style="padding-top:10px;padding-left:10px; width: 50%;">
|
||||
<table cellspacing="0" cellpadding="0" border="0">
|
||||
[{block name="user_d3user_totp_form1"}]
|
||||
[{if false == $totp->getId()}]
|
||||
<tr>
|
||||
<td class="edittext" colspan="2">
|
||||
<h4>[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]</h4>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
[{oxmultilang ident="D3_TOTP_QRCODE"}]
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<img src="[{$totp->getQrCodeUri()}]">
|
||||
[{oxinputhelp ident="D3_TOTP_QRCODE_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET"}]</label>
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
|
||||
[{oxinputhelp ident="D3_TOTP_SECRET_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
<label for="pwd">[{oxmultilang ident="D3_TOTP_CURRPWD"}]</label>
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<input type="password" class="editinput" size="15" id="pwd" name="pwd" value="" [{$readonly}]>
|
||||
[{oxinputhelp ident="D3_TOTP_CURRPWD_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" [{$readonly}]>
|
||||
[{oxinputhelp ident="D3_TOTP_CURROTP_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
[{else}]
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
|
||||
</td>
|
||||
<td class="edittext">
|
||||
neuen Zugang anlegen, alle bisherigen Zugännge werden damit ungültig
|
||||
</td>
|
||||
</tr>
|
||||
[{/if}]
|
||||
</td>
|
||||
</tr>
|
||||
</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">
|
||||
[{block name="user_d3user_totp_form2"}]
|
||||
|
||||
[{/block}]
|
||||
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
[{oxmultilang ident="D3_TOTP_QRCODE"}]
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<img src="[{$edit->getQrCodeUri()}]">
|
||||
[{*
|
||||
<input type="text" class="editinput" size="32" maxlength="[{$edit->oxarticles__oxtitle->fldmax_length}]" id="oLockTarget" name="editval[oxarticles__oxtitle]" value="[{$edit->oxarticles__oxtitle->value}]">
|
||||
[{oxinputhelp ident="HELP_ARTICLE_MAIN_TITLE"}]
|
||||
*}]
|
||||
<td class="edittext" colspan="2"><br><br>
|
||||
<input type="submit" class="edittext" id="oLockButton" name="saveArticle" value="[{oxmultilang ident="ARTICLE_MAIN_SAVE"}]" onClick="document.myedit.fnc.value='save'" [{$readonly}]>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
[{oxmultilang ident="D3_TOTP_SECRET"}]
|
||||
</td>
|
||||
<td class="edittext">
|
||||
[{$edit->getSecret()}]
|
||||
[{*
|
||||
<input type="text" class="editinput" size="32" maxlength="[{$edit->oxarticles__oxartnum->fldmax_length}]" name="editval[oxarticles__oxartnum]" value="[{$edit->oxarticles__oxartnum->value}]" [{$readonly}]>
|
||||
[{oxinputhelp ident="HELP_ARTICLE_MAIN_ARTNUM"}]
|
||||
*}]
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="edittext">
|
||||
[{oxmultilang ident="D3_TOTP_CURROTP"}]
|
||||
</td>
|
||||
<td class="edittext">
|
||||
<input type="text" class="editinput" size="6" maxlength="6" name="otp" value="">
|
||||
[{oxinputhelp ident="D3_TOTP_CURROTP_HELP"}]
|
||||
</td>
|
||||
</tr>
|
||||
[{/block}]
|
||||
</table>
|
||||
</td>
|
||||
<!-- Ende rechte Seite -->
|
||||
</tr>
|
||||
</table>
|
||||
</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">
|
||||
[{block name="user_d3user_totp_form2"}][{/block}]
|
||||
</table>
|
||||
</td>
|
||||
<!-- Ende rechte Seite -->
|
||||
</tr>
|
||||
</table>
|
||||
[{/if}]
|
||||
</form>
|
||||
|
||||
[{include file="bottomnaviitem.tpl"}]
|
||||
|
@ -72,6 +72,7 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
|
||||
$return = parent::checklogin();
|
||||
} elseif ($this->hasValidTotp($sTotp, $totp)) {
|
||||
Registry::getSession()->setVariable(d3totp::TOTP_SESSION_VARNAME, $sTotp);
|
||||
Registry::getSession()->deleteVariable('pwdTransmit');
|
||||
$return = "admin_start";
|
||||
}
|
||||
} catch (d3totp_wrongOtpException $oEx) {
|
||||
|
@ -16,6 +16,9 @@
|
||||
namespace D3\Totp\Modules\Application\Model;
|
||||
|
||||
use D3\Totp\Application\Model\d3totp;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use OxidEsales\Eshop\Core\DatabaseProvider;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
|
||||
class d3_totp_user extends d3_totp_user_parent
|
||||
@ -29,4 +32,51 @@ class d3_totp_user extends d3_totp_user_parent
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUserId
|
||||
* @param $sPassword
|
||||
* @return bool
|
||||
* @throws DatabaseConnectionException
|
||||
*/
|
||||
public function d3CheckPasswordPass($sUserId, $sPassword)
|
||||
{
|
||||
return (bool) DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getOne(
|
||||
$this->d3GetPasswordCheckQuery($sUserId, $sPassword)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUserId
|
||||
* @param $sPassword
|
||||
* @return string
|
||||
* @throws DatabaseConnectionException
|
||||
*/
|
||||
public function d3GetPasswordCheckQuery($sUserId, $sPassword)
|
||||
{
|
||||
$oDb = \OxidEsales\Eshop\Core\DatabaseProvider::getDb();
|
||||
|
||||
$sUserSelect = "oxuser.oxid = " . $oDb->quote($sUserId);
|
||||
|
||||
$sSalt = $oDb->getOne("SELECT `oxpasssalt` FROM `oxuser` WHERE " . $sUserSelect);
|
||||
|
||||
$sPassSelect = " oxuser.oxpassword = " . $oDb->quote($this->encodePassword($sPassword, $sSalt));
|
||||
|
||||
$sSelect = "select `oxid` from oxuser where 1 and {$sPassSelect} and {$sUserSelect} ";
|
||||
|
||||
return $sSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return d3totp
|
||||
* @throws DatabaseConnectionException
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function d3getTotp()
|
||||
{
|
||||
$oTotp = oxNew(d3totp::class);
|
||||
$oTotp->loadByUserId($this->getId());
|
||||
|
||||
return $oTotp;
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ $aModule = [
|
||||
'email' => 'support@shopmodule.com',
|
||||
'url' => 'http://www.oxidmodule.com/',
|
||||
'extend' => [
|
||||
//OxidModel\User::class => \D3\Totp\Modules\Application\Model\d3_totp_user::class,
|
||||
OxidModel\User::class => \D3\Totp\Modules\Application\Model\d3_totp_user::class,
|
||||
LoginController::class => \D3\Totp\Modules\Application\Controller\Admin\d3_totp_LoginController::class,
|
||||
Utils::class => \D3\Totp\Modules\Core\d3_totp_utils::class,
|
||||
],
|
||||
|
Laden…
Verwijs in nieuw issue
Block a user