Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to select enforced method #47

Merged
merged 5 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ public function getEnforcedGroups()
return empty($enforcedGroups) ? [] : explode(',', $enforcedGroups);
}

/**
* Get default method for the mandatory/enforced groups
*
* @return string
*/
public function getEnforcedMethod(): string
{
return $this->settings->get('enforcedMethod', $this->defaultDriver);
}

/**
* @return mixed
*/
Expand Down
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
=========

1.1.0 (Unreleased)
---------------------
- Enh #41: Added Option to use Google Authentication as Default

1.0.7 (March 2, 2022)
---------------------
- Fix #45: Fix remember browser
Expand Down
103 changes: 86 additions & 17 deletions drivers/GoogleAuthenticatorDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@

namespace humhub\modules\twofa\drivers;

use humhub\modules\twofa\assets\Assets;
use humhub\modules\twofa\helpers\TwofaHelper;
use humhub\modules\twofa\models\CheckCode;
use Sonata\GoogleAuthenticator\GoogleAuthenticator;
use Yii;
use yii\bootstrap\ActiveForm;

class GoogleAuthenticatorDriver extends BaseDriver
{
Expand Down Expand Up @@ -47,20 +50,45 @@ class_exists('\Sonata\GoogleAuthenticator\GoogleQrUrl');
*/
public function send()
{
if (parent::isActive() && TwofaHelper::isEnforcedUser()) {
return true;
}

if (!$this->beforeSend()) {
return false;
}

$secret = TwofaHelper::getSetting(self::SECRET_SETTING);
if (empty($secret))
{ // If secret code is empty then QR code was not generated,
if (empty($secret)) {
// If secret code is empty then QR code was not generated,
// so current User cannot use this Driver for 2FA
return false;
}

return true;
}

/**
* @inheritdoc
*/
public function beforeCheckCodeFormInput(ActiveForm $form, CheckCode $model)
{
if ($this->isActive() && !empty(TwofaHelper::getSetting(self::SECRET_SETTING))) {
parent::beforeCheckCodeFormInput($form, $model);
return;
}

Assets::register(Yii::$app->view);

$this->generateTempSecretCode();
echo $this->getQrCodeSecretKeyFile([
'requirePinCode' => true,
'columnLeftClass' => 'col-md-12',
'columnRightClass' => 'col-md-12',
'codeSize' => 370,
]);
}

/**
* Render additional user settings
*
Expand All @@ -77,9 +105,20 @@ public function renderUserSettings($params)
]
]);

$model = $this->getUserSettings();

if (TwofaHelper::getSetting(GoogleAuthenticatorDriver::SECRET_SETTING) === null) {
// Display a form to request new code when current user group is forced for this Driver
$requirePinCode = true;
$this->generateTempSecretCode();
} else {
$requirePinCode = $model->hasErrors('pinCode');
}

$this->renderUserSettingsFile(array_merge($params, [
'driver' => $this,
'model' => $this->getUserSettings(),
'model' => $model,
'requirePinCode' => $requirePinCode,
]));
}

Expand All @@ -92,11 +131,24 @@ public function renderUserSettings($params)
*/
public function checkCode($verifyingCode, $correctCode = null)
{
$isNewCodeAfterLogin = false;
if ($correctCode === null) {
$correctCode = TwofaHelper::getSetting(self::SECRET_SETTING);
if ($correctCode === null && TwofaHelper::isEnforcedUser()) {
$correctCode = TwofaHelper::getSetting(self::SECRET_TEMP_SETTING);
$isNewCodeAfterLogin = true;
}
}

return $this->getGoogleAuthenticator()->checkCode($correctCode, $verifyingCode);
$result = $this->getGoogleAuthenticator()->checkCode($correctCode, $verifyingCode);

if ($result && $isNewCodeAfterLogin) {
TwofaHelper::setSetting(TwofaHelper::USER_SETTING, self::class);
TwofaHelper::setSetting(self::SECRET_SETTING, $correctCode);
TwofaHelper::setSetting(self::SECRET_TEMP_SETTING);
}

return $result;
}

/**
Expand All @@ -111,41 +163,58 @@ protected function getGoogleAuthenticator()
}

/**
* Request code by AJAX request on user settings form
* Generate new secret code and store for current User
*
* @param array Params
* @return string
*/
public function actionRequestCode($params)
protected function generateTempSecretCode(): string
{
// Generate new secret code and store for current User:
$secret = $this->getGoogleAuthenticator()->generateSecret();

// Save new generated secret in temporary setting before confirm by pin code:
TwofaHelper::setSetting(self::SECRET_TEMP_SETTING, $secret);

return $this->getQrCodeSecretKeyFile(true);
return $secret;
}

/**
* Request code by AJAX request on user settings form
*
* @return string
*/
public function actionRequestCode()
{
// Save new generated secret in temporary setting before confirm by pin code:
$this->generateTempSecretCode();

return $this->getQrCodeSecretKeyFile(['requirePinCode' => true]);
}

/**
* Get file with QR code and secret key
*
* @param boolean Require pin code?
* @param array $params
* @return string|void
* @throws \Throwable
*/
public function getQrCodeSecretKeyFile($requirePinCode = false)
public function getQrCodeSecretKeyFile($params = [])
{
$secret = TwofaHelper::getSetting($requirePinCode ? self::SECRET_TEMP_SETTING : self::SECRET_SETTING);
$params = array_merge([
'requirePinCode' => false,
'columnLeftClass' => 'col-md-6',
'columnRightClass' => 'col-md-6',
'codeSize' => 300,
], $params);

$secret = TwofaHelper::getSetting($params['requirePinCode'] ? self::SECRET_TEMP_SETTING : self::SECRET_SETTING);

if (empty($secret)) {
return '';
}

return $this->renderFile([
'qrCodeText' => 'otpauth://totp/' . Yii::$app->request->hostName . ':' . rawurlencode(TwofaHelper::getAccountName()) . '?secret=' . $secret . '&issuer=' . Yii::$app->request->hostName,
'secret' => $secret,
'requirePinCode' => $requirePinCode,
], ['suffix' => 'Code']);
$params['qrCodeText'] = 'otpauth://totp/' . Yii::$app->request->hostName . ':' . rawurlencode(TwofaHelper::getAccountName()) . '?secret=' . $secret . '&issuer=' . Yii::$app->request->hostName;
$params['secret'] = $secret;

return $this->renderFile($params, ['suffix' => 'Code']);
}
}
2 changes: 1 addition & 1 deletion helpers/TwofaHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static function getDriverSetting()
}

if (empty($driverClass) && self::isEnforcedUser()) {
return $module->defaultDriver;
return $module->getEnforcedMethod();
}

return $driverClass;
Expand Down
9 changes: 9 additions & 0 deletions models/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class Config extends Model
*/
public $enforcedGroups;

/**
* @var string Method that is used for enforcing
*/
public $enforcedMethod;

/**
* @var int Length of verifying code
*/
Expand Down Expand Up @@ -53,6 +58,7 @@ public function init()
$this->codeLength = $this->module->getCodeLength();
$this->rememberMeDays = $this->module->getRememberMeDays();
$this->enforcedGroups = $this->module->getEnforcedGroups();
$this->enforcedMethod = $this->module->getEnforcedMethod();
$this->trustedNetworks = implode(', ', $this->module->getTrustedNetworks());
}

Expand All @@ -66,6 +72,7 @@ public function rules()
['codeLength', 'integer', 'min' => 4],
['rememberMeDays', 'integer', 'max' => 365],
['enforcedGroups', 'in', 'range' => array_keys($this->module->getGroupsOptions()), 'allowArray' => true],
['enforcedMethod', 'in', 'range' => array_keys($this->module->getDriversOptions())],
['trustedNetworks', 'string']
];
}
Expand All @@ -82,6 +89,7 @@ public function attributeLabels()
'codeLength' => Yii::t('TwofaModule.config', 'Length of verifying code'),
'rememberMeDays' => Yii::t('TwofaModule.config', 'Remember browser option amount of days'),
'enforcedGroups' => Yii::t('TwofaModule.config', 'Mandatory for the following groups'),
'enforcedMethod' => Yii::t('TwofaModule.config', 'Default method for the mandatory groups'),
'trustedNetworks' => Yii::t('TwofaModule.config', 'Trusted networks list'),
];
}
Expand All @@ -97,6 +105,7 @@ public function save()

$this->module->settings->set('enabledDrivers', empty($this->enabledDrivers) ? '' : implode(',', $this->enabledDrivers));
$this->module->settings->set('enforcedGroups', empty($this->enforcedGroups) ? '' : implode(',', $this->enforcedGroups));
$this->module->settings->set('enforcedMethod', $this->enforcedMethod);
$this->module->settings->set('codeLength', $this->codeLength);
$this->module->settings->set('rememberMeDays', $this->rememberMeDays);
$this->module->settings->set('trustedNetworks', json_encode($this->getTrustedNetworksArray()));
Expand Down
1 change: 0 additions & 1 deletion views/check/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
<?php if ($rememberDays): ?>
<?= $form->field($model, 'rememberBrowser')->checkbox()
->label(Yii::t('TwofaModule.base', 'Remember this browser for {0} days', [$rememberDays])) ?>

<?php endif; ?>

<br/>
Expand Down
6 changes: 1 addition & 5 deletions views/config/module.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ class="alert alert-warning"<?= empty($model->enabledDrivers) ? '' : ' style="dis

<?= $form->field($model, 'enforcedGroups')->checkboxList($model->module->getGroupsOptions()); ?>

<div class="help-block">
<?= Yii::t('TwofaModule.config', 'By default, the method "{defaultDriverName}" is used.', [
'defaultDriverName' => $defaultDriverName
]) ?></div>
<br/>
<?= $form->field($model, 'enforcedMethod')->dropDownList($model->module->getDriversOptions()); ?>

<?= $form->field($model, 'codeLength'); ?>

Expand Down
12 changes: 6 additions & 6 deletions views/config/userGoogleAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
* @license https://www.humhub.com/licences
*/

/* @var $driver GoogleAuthenticatorDriver */
/* @var $form ActiveForm */
/* @var $model GoogleAuthenticatorUserSettings */

use humhub\modules\twofa\drivers\GoogleAuthenticatorDriver;
use humhub\modules\twofa\models\GoogleAuthenticatorUserSettings;
use humhub\widgets\Button;
use yii\bootstrap\ActiveForm;

/* @var $driver GoogleAuthenticatorDriver */
/* @var $form ActiveForm */
/* @var $model GoogleAuthenticatorUserSettings */
/* @var $requirePinCode bool */
?>
<div id="twofaGoogleAuthCode" class="form-group">
<?= $driver->getQrCodeSecretKeyFile($model->hasErrors('pinCode')) ?>
<?= $driver->getQrCodeSecretKeyFile(['requirePinCode' => $requirePinCode]) ?>
</div>

<div id="twofaGoogleAuthPinCode"<?= $model->hasErrors('pinCode') ? '' : ' style="display:none"' ?>>
<div id="twofaGoogleAuthPinCode"<?= $requirePinCode ? '' : ' style="display:none"' ?>>
<?= $form->field($model, 'pinCode') ?>
<?= $form->field($model, 'changeSecretCode')->hiddenInput()->label(false) ?>
</div>
Expand Down
20 changes: 9 additions & 11 deletions views/config/userGoogleAuthenticatorCode.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
<?php

/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2020 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/

use humhub\modules\twofa\helpers\TwofaHelper;

/* @var $qrCodeText string */
/* @var $secret string */
/* @var $requirePinCode boolean */

use humhub\modules\twofa\helpers\TwofaHelper;

/* @var $columnLeftClass string */
/* @var $columnRightClass string */
/* @var $codeSize integer */
?>
<p><?= Yii::t('TwofaModule.config', 'Install an application that implements a time-based one-time password (TOTP) algorithm, such as {googleAuthenticatorLink}, and use it to scan the QR code shown below.',
['{googleAuthenticatorLink}' => '<a href="https://support.google.com/accounts/answer/1066447" target="_blank">' . Yii::t('TwofaModule.config', 'Google Authenticator'). '</a>']); ?></p>

<div class="row">
<div class="col-md-6">
<div class="<?= $columnLeftClass ?>">
<div class="form-group">
<div id="twofa-google-auth-qrcode"></div>
<div class="help-block"></div>
</div>
</div>
<div class="col-md-6">
<div class="<?= $columnRightClass ?>">
<div class="alert alert-default">
<p><strong><?= Yii::t('TwofaModule.config', 'Can\'t scan the code?'); ?></strong></p>
<br/>
Expand All @@ -35,19 +36,16 @@
<?= Yii::t('TwofaModule.config', 'Time based: Yes'); ?><br>
</p>
</div>
<br/>

</div>

</div>

<script>
$(document).ready(function(){
if ($('#twofa-google-auth-qrcode').html() === '') {
new QRCode('twofa-google-auth-qrcode', {
text: '<?= $qrCodeText ?>',
width: 300,
height: 300,
width: <?= $codeSize ?>,
height: <?= $codeSize ?>,
correctLevel: QRCode.CorrectLevel.L
});
}
Expand Down