implement OTP check, add exception

This commit is contained in:
Daniel Seifert 2018-10-18 15:33:59 +02:00
parent ed3eee4e67
commit 0a528f993b
8 changed files with 372 additions and 27 deletions

View File

@ -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;
}
}

View File

@ -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
{
}

View 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;
}
}

View File

@ -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',
];

View File

@ -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}]>&nbsp;&nbsp;&nbsp;
[{/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"}]&nbsp;
</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"}]&nbsp;
</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"}]&nbsp;
</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"}]

View File

@ -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)
);
}
}

View File

@ -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;
}
}

View File

@ -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;
}