forked from D3Public/oxtotp
implement OTP check, add exception
This commit is contained in:
parent
ed3eee4e67
commit
0a528f993b
@ -15,9 +15,36 @@
|
||||
|
||||
namespace D3\Totp\Application\Controller\Admin;
|
||||
|
||||
use D3\Totp\Application\Model\d3totp;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
|
||||
class d3user_totp extends AdminDetailsController
|
||||
{
|
||||
protected $_sThisTemplate = 'd3user_totp.tpl';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws DBALException
|
||||
* @throws DatabaseConnectionException
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
parent::render();
|
||||
|
||||
$soxId = $this->_aViewData["oxid"] = $this->getEditObjectId();
|
||||
if (isset($soxId) && $soxId != "-1") {
|
||||
/** @var d3totp $oTotp */
|
||||
$oTotp = oxNew(d3totp::class);
|
||||
$oTotp->loadByUserId($soxId);
|
||||
$this->_aViewData["edit"] = $oTotp;
|
||||
}
|
||||
|
||||
if (!$this->_allowAdminEdit($soxId)) {
|
||||
$this->_aViewData['readonly'] = true;
|
||||
}
|
||||
|
||||
return $this->_sThisTemplate;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?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\ModCfg\Application\Model\DependencyInjectionContainer\d3DicHandler;
|
||||
use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception;
|
||||
use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException;
|
||||
use D3\ModCfg\Application\Model\Log\d3log;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
|
||||
use OxidEsales\Eshop\Core\Exception\StandardException;
|
||||
|
||||
class d3totp_wrongOtpException extends StandardException
|
||||
{
|
||||
|
||||
}
|
150
src/Application/Model/d3totp.php
Normal file
150
src/Application/Model/d3totp.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?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 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\DatabaseProvider;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
use OxidEsales\Eshop\Core\Model\BaseModel;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
|
||||
class d3totp extends BaseModel
|
||||
{
|
||||
public $tableName = 'd3totp';
|
||||
public $userId;
|
||||
public $totp;
|
||||
|
||||
/**
|
||||
* d3totp constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->init($this->tableName);
|
||||
|
||||
return parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $userId
|
||||
* @return bool
|
||||
* @throws DBALException
|
||||
* @throws DatabaseConnectionException
|
||||
*/
|
||||
public function loadByUserId($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$oQB = d3database::getInstance()->getQueryBuilder();
|
||||
$oQB->select('oxid')
|
||||
->from($this->getViewName())
|
||||
->where("oxuserid = ".$oQB->createNamedParameter($userId))
|
||||
->setMaxResults(1);
|
||||
|
||||
return $this->load(DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getOne($oQB->getSQL(), $oQB->getParameters()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
$userId = $this->userId ? $this->userId : $this->getFieldData('oxuserid');
|
||||
|
||||
$user = oxNew(User::class);
|
||||
$user->load($userId);
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function UserUseTotp()
|
||||
{
|
||||
return $this->getFieldData('usetotp');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $userId
|
||||
* @return string
|
||||
*/
|
||||
public function getSavedSecret()
|
||||
{
|
||||
$secret = $this->getFieldData('seed');
|
||||
|
||||
if ($secret) {
|
||||
return $secret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TOTP
|
||||
*/
|
||||
public function getTotp()
|
||||
{
|
||||
if (false == $this->totp) {
|
||||
$this->totp = oxNew(
|
||||
TOTP::class,
|
||||
$this->getUser()->getFieldData('oxusername')
|
||||
? $this->getUser()->getFieldData('oxusername')
|
||||
: null,
|
||||
$this->getSavedSecret()
|
||||
);
|
||||
$this->totp->setIssuer(Registry::getConfig()->getActiveShop()->getFieldData('oxname'));
|
||||
}
|
||||
|
||||
return $this->totp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQrCodeUri()
|
||||
{
|
||||
return $this->getTotp()->getQrCodeUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSecret()
|
||||
{
|
||||
return $this->getTotp()->getSecret();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $totp
|
||||
* @return string
|
||||
* @throws d3totp_wrongOtpException
|
||||
*/
|
||||
public function verify($totp)
|
||||
{
|
||||
$blVerify = $this->getTotp()->verify($totp, null, 2);
|
||||
if (false == $blVerify) {
|
||||
$oException = oxNew(d3totp_wrongOtpException::class, 'unvalid TOTP');
|
||||
throw $oException;
|
||||
}
|
||||
|
||||
return $blVerify;
|
||||
}
|
||||
}
|
@ -24,4 +24,10 @@ $aLang = [
|
||||
'TOTP_INPUT_HELP' => 'Den Authentisierungscode erhalten Sie von der Zweifaktorauthentisierung-App auf Ihrem Gerät.',
|
||||
|
||||
'd3mxuser_totp' => '2-Faktor-Authentisierung',
|
||||
|
||||
'D3_TOTP_ACTIVE' => 'Benutzer verwendet 2-Faktor-Authentisierung',
|
||||
'D3_TOTP_ACTIVE_HELP' => 'Benutzer verwendet 2-Faktor-Authentisierung',
|
||||
'D3_TOTP_QRCODE' => 'QR-Code',
|
||||
'D3_TOTP_SECRET' => 'QR-Code kann nicht gescannt werden?',
|
||||
'D3_TOTP_CURROTP' => 'Bestätigung mit Einmalpasswort',
|
||||
];
|
||||
|
@ -1,6 +1,97 @@
|
||||
[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign}]
|
||||
|
||||
foo
|
||||
[{if $readonly}]
|
||||
[{assign var="readonly" value="readonly disabled"}]
|
||||
[{else}]
|
||||
[{assign var="readonly" value=""}]
|
||||
[{/if}]
|
||||
|
||||
<form name="transfer" id="transfer" action="[{$oViewConf->getSelfLink()}]" method="post">
|
||||
[{$oViewConf->getHiddenSid()}]
|
||||
<input type="hidden" name="oxid" value="[{$oxid}]">
|
||||
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
|
||||
</form>
|
||||
|
||||
<form name="myedit" id="myedit" action="[{$oViewConf->getSelfLink()}]" method="post" style="padding: 0;margin: 0;height:0;">
|
||||
[{$oViewConf->getHiddenSid()}]
|
||||
<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__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}]
|
||||
|
||||
<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}]
|
||||
</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"}]
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
[{include file="bottomnaviitem.tpl"}]
|
||||
[{include file="bottomitem.tpl"}]
|
@ -16,6 +16,7 @@
|
||||
namespace D3\Totp\Modules\Application\Controller\Admin;
|
||||
|
||||
use D3\Totp\Application\Model\d3totp;
|
||||
use D3\Totp\Application\Model\Exceptions\d3totp_wrongOtpException;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
@ -33,12 +34,16 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
|
||||
|
||||
$return = parent::render();
|
||||
|
||||
$totp = oxNew(d3totp::class);
|
||||
$totp->loadByUserId($auth);
|
||||
|
||||
if ($auth
|
||||
&& oxNew(d3totp::class)->UserUseTotp($auth)
|
||||
&& $totp->UserUseTotp()
|
||||
&& false == Registry::getSession()->getVariable("totp_auth")
|
||||
) {
|
||||
// set auth as secured parameter;
|
||||
$return = 'd3login_totp.tpl';
|
||||
Registry::getSession()->setVariable("auth", $auth);
|
||||
$this->addTplParam('request_totp', true);
|
||||
}
|
||||
|
||||
return $return;
|
||||
@ -51,19 +56,48 @@ class d3_totp_LoginController extends d3_totp_LoginController_parent
|
||||
*/
|
||||
public function checklogin()
|
||||
{
|
||||
$return = parent::checklogin();
|
||||
$sTotp = Registry::getRequest()->getRequestEscapedParameter('d3totp', true);
|
||||
|
||||
if ($return == "admin_start") {
|
||||
if ((bool) $this->getSession()->checkSessionChallenge()
|
||||
&& count(\OxidEsales\Eshop\Core\Registry::getUtilsServer()->getOxCookie())
|
||||
&& Registry::getSession()->getVariable("auth")
|
||||
&& oxNew(d3totp::class)->UserUseTotp(Registry::getSession()->getVariable("auth"))
|
||||
&& false == Registry::getSession()->getVariable("totp_auth")
|
||||
) {
|
||||
$return = "login";
|
||||
$totp = oxNew(d3totp::class);
|
||||
$totp->loadByUserId(Registry::getSession()->getVariable("auth"));
|
||||
|
||||
$return = 'login';
|
||||
|
||||
try {
|
||||
if ($this->isNoTotpOrNoLogin($totp)) {
|
||||
$return = parent::checklogin();
|
||||
} elseif ($this->hasValidTotp($sTotp, $totp)) {
|
||||
Registry::getSession()->setVariable('totp_auth', $sTotp);
|
||||
$return = "admin_start";
|
||||
}
|
||||
} catch (d3totp_wrongOtpException $oEx) {
|
||||
Registry::getUtilsView()->addErrorToDisplay($oEx);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param d3totp $totp
|
||||
* @return bool
|
||||
*/
|
||||
public function isNoTotpOrNoLogin($totp)
|
||||
{
|
||||
return false == Registry::getSession()->getVariable("auth")
|
||||
|| false == $totp->UserUseTotp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTotp
|
||||
* @param d3totp $totp
|
||||
* @return bool
|
||||
* @throws d3totp_wrongOtpException
|
||||
*/
|
||||
public function hasValidTotp($sTotp, $totp)
|
||||
{
|
||||
return Registry::getSession()->getVariable("totp_auth") ||
|
||||
(
|
||||
$sTotp && $totp->verify($sTotp)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,27 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: DanielSeifert
|
||||
* Date: 17.10.2018
|
||||
* Time: 12:03
|
||||
* 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\Modules\Application\Model;
|
||||
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
|
||||
class d3_totp_user extends d3_totp_user_parent
|
||||
{
|
||||
public function d3UseTotp()
|
||||
public function logout()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$return = parent::logout();
|
||||
|
||||
public function d3GetTotpSecret()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// deleting session info
|
||||
Registry::getSession()->deleteVariable('totp_auth');
|
||||
|
||||
public function d3SetTotpSecret()
|
||||
{
|
||||
return false;
|
||||
return $return;
|
||||
}
|
||||
}
|
@ -35,8 +35,9 @@ class d3_totp_utils extends d3_totp_utils_parent
|
||||
$totpAuth = (bool) Registry::getSession()->getVariable("totp_auth");
|
||||
/** @var d3totp $totp */
|
||||
$totp = oxNew(d3totp::class);
|
||||
$totp->loadByUserId($userID);
|
||||
|
||||
if (1 == 0 && $blAuth && $totp->UserUseTotp($userID) && false === $totpAuth) {
|
||||
if ($blAuth && $totp->UserUseTotp() && false === $totpAuth) {
|
||||
Registry::getUtils()->redirect('index.php?cl=login', true, 302);
|
||||
exit;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user