Skip to content
Code-Schnipsel Gruppen Projekte
Commit cbbacb88 erstellt von Patrick Müller's avatar Patrick Müller
Dateien durchsuchen

feat: Neu-Generierung von Recovery Keys

Übergeordneter 2a1f231c
No related branches found
No related tags found
Keine zugehörigen Merge Requests gefunden
......@@ -39,8 +39,8 @@ function ($userId, $title) {
$keyData['key'] = Security::decrypt($secrets[$title]['key']);
$keyData['qrCode'] = $Google2FA->getQRCodeInline(
'QUIQQER',
$AuthUser->getAttribute('email'),
$_SERVER['SERVER_NAME'],
$AuthUser->getUsername(),
$keyData['key']
);
......@@ -50,10 +50,10 @@ function ($userId, $title) {
$keyData['recoveryKeys'] = array();
foreach ($secrets[$title]['recoveryKeys'] as $recoveryCode) {
$keyData['recoveryKeys'][] = trim(Security::decrypt($recoveryCode));
foreach ($secrets[$title]['recoveryKeys'] as $k => $recoveryKeyData) {
$recoveryKeyData['key'] = trim(Security::decrypt($recoveryKeyData['key']));
$keyData['recoveryKeys'][] = $recoveryKeyData;
}
} catch (QUI\Auth\Google2Fa\Exception $Exception) {
QUI::getMessagesHandler()->addError(
QUI::getLocale()->get(
......
<?php
use QUI;
use PragmaRX\Google2FA\Google2FA;
use QUI\Utils\Security\Orthos;
use QUI\Security;
use QUI\Auth\Google2Fa\Auth;
/**
* Re-generate a set of recovery keys for a user authentication key
*
* @param string $title - key title
* @return bool - success
*/
QUI::$Ajax->registerFunction(
'package_quiqqer_authgoogle2fa_ajax_regenerateRecoveryKeys',
function ($userId, $title) {
$AuthUser = QUI::getUsers()->get((int)$userId);
$title = Orthos::clear($title);
$EditUser = QUI::getUserBySession();
// @todo Check user edit permission of session user
try {
$secrets = json_decode($AuthUser->getAttribute('quiqqer.auth.google2fa.secrets'), true);
if (empty($secrets)) {
$secrets = array();
}
if (!isset($secrets[$title])) {
throw new QUI\Auth\Google2Fa\Exception(array(
'quiqqer/authgoogle2fa',
'exception.ajax.getKey.title.not.found',
array(
'title' => $title,
'user' => $AuthUser->getUsername(),
'userId' => $AuthUser->getId()
)
));
}
$secrets[$title]['recoveryKeys'] = Auth::generateRecoveryKeys();
$AuthUser->setAttribute(
'quiqqer.auth.google2fa.secrets',
json_encode($secrets)
);
$AuthUser->save();
} catch (QUI\Auth\Google2Fa\Exception $Exception) {
QUI::getMessagesHandler()->addError(
QUI::getLocale()->get(
'quiqqer/authgoogle2fa',
'message.ajax.regenerateRecoveryKeys.error',
array(
'error' => $Exception->getMessage()
)
)
);
return false;
} catch (\Exception $Exception) {
QUI::getMessagesHandler()->addError(
QUI::getLocale()->get(
'quiqqer/authgoogle2fa',
'message.ajax.general.error'
)
);
return false;
}
QUI::getMessagesHandler()->addSuccess(
QUI::getLocale()->get(
'quiqqer/authgoogle2fa',
'message.ajax.regenerateRecoveryKeys.success',
array(
'title' => $title
)
)
);
return true;
},
array('userId', 'title'),
'Permission::checkAdminUser'
);
<table class="quiqqer-auth-google2fa-register-showkey-data data-table data-table-flexbox">
<thead>
<tr>
<th colspan="2">
{{tableHeader}}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
......@@ -42,12 +34,13 @@
</tr>
<tr>
<td>
<label class="field-container">
<div class="field-container">
<span class="field-container-item">{{labelRecoveryKeys}}</span>
<div class="field-container-field quiqqer-auth-google2fa-register-showkey-recoverykeys">
<ul></ul>
<div class="quiqqer-auth-google2fa-register-showkey-recoverykeys-regenerate-btn"></div>
</div>
</label>
</div>
</td>
</tr>
</tbody>
......
......@@ -27,4 +27,12 @@
.quiqqer-auth-google2fa-register-generatekey input {
width: 100%;
}
.quiqqer-auth-google2fa-register-showkey-data .field-container-item {
width: 150px;
}
.quiqqer-auth-google2fa-register-showkey-recoverykeys ul {
padding-left: 20px;
}
\ No newline at end of file
......@@ -156,10 +156,6 @@ define('package/quiqqer/authgoogle2fa/bin/controls/Settings', [
* Event: onInject
*/
$onInject: function () {
console.log(this.getAttribute('uid'));
console.log(this.getElm().get('data-qui-options-uid'));
this.resize();
this.refresh();
},
......@@ -309,12 +305,65 @@ define('package/quiqqer/authgoogle2fa/bin/controls/Settings', [
var KeyData;
var Row = this.$Grid.getDataByRow(row);
var FuncShowRegenerateWarning = function () {
var WarnPopup = new QUIConfirm({
title : QUILocale.get(lg, 'controls.settings.showkey.regenerate.warning.title'),
maxHeight : 200,
maxWidth : 500,
icon : 'fa fa-repeat',
backgroundClosable: false,
// buttons
buttons : true, // {bool} [optional] show the bottom button line
//closeButtonText : Locale.get('qui/controls/windows/Popup', 'btn.close'),
titleCloseButton: false, // {bool} show the title close button
content : false,
events : {
onOpen : function () {
Popup.Loader.show();
var Content = WarnPopup.getContent();
Content.set(
'html',
QUILocale.get(lg, 'controls.settings.showkey.regenerate.warning')
);
},
onSubmit: function () {
QUIAjax.post(
'package_quiqqer_authgoogle2fa_ajax_regenerateRecoveryKeys',
function (success) {
Popup.Loader.hide();
WarnPopup.close();
if (!success) {
return;
}
Popup.close();
self.$showKey(row);
}, {
'package': 'quiqqer/authgoogle2fa',
title : Row.title,
userId : self.getAttribute('uid')
}
);
},
onClose : function () {
Popup.Loader.hide();
}
}
});
WarnPopup.open();
};
var Popup = new QUIConfirm({
title : QUILocale.get(
lg, 'controls.settings.showkey.title'
),
maxHeight : 720,
maxWidth : 650,
title : QUILocale.get(lg, 'controls.settings.showkey.template.tableHeader', {
title: Row.title
}),
maxHeight : 710,
maxWidth : 665,
icon : 'fa fa-key', // {false|string} [optional] icon of the window
backgroundClosable: true, // {bool} [optional] closes the window on click? standard = true
......@@ -334,9 +383,6 @@ define('package/quiqqer/authgoogle2fa/bin/controls/Settings', [
key : KeyData.key,
createUser : KeyData.createUser,
createDate : KeyData.createDate,
tableHeader : QUILocale.get(lg, lgPrefix + 'tableHeader', {
title: Row.title
}),
labelQrCode : QUILocale.get(lg, lgPrefix + 'labelQrCode'),
labelKey : QUILocale.get(lg, lgPrefix + 'labelKey'),
labelCreateUser : QUILocale.get(lg, lgPrefix + 'labelCreateUser'),
......@@ -360,10 +406,35 @@ define('package/quiqqer/authgoogle2fa/bin/controls/Settings', [
);
for (var i = 0, len = KeyData.recoveryKeys.length; i < len; i++) {
new Element('li', {
html: KeyData.recoveryKeys[i]
}).inject(RecoveryListElm);
var RecoveryKeyData = KeyData.recoveryKeys[i];
var LiElm = new Element('li').inject(RecoveryListElm);
var KeyTextElm = new Element('span', {
html: RecoveryKeyData.key
}).inject(LiElm);
if (RecoveryKeyData.used) {
KeyTextElm.setStyle('text-decoration', 'line-through');
new Element('span', {
html: ' (' + RecoveryKeyData.usedDate + ')'
}).inject(LiElm);
}
}
// Re-generate recovery keys btn
new QUIButton({
textimage: 'fa fa-repeat',
text : QUILocale.get(lg, 'controls.settings.showkey.regenerate.recoverykeys.btn'),
events : {
onClick: FuncShowRegenerateWarning
}
}).inject(
Content.getElement(
'.quiqqer-auth-google2fa-register-showkey-recoverykeys-regenerate-btn'
)
)
}
}
});
......
......@@ -24,10 +24,10 @@
<de><![CDATA[Beim Verarbeiten der Anfrage ist ein unerwarteter Fehler aufgetreten. Bitte wenden Sie sich an einen Administrator.]]></de>
</locale>
<locale name="message.ajax.generateKey.error" html="true">
<de><![CDATA[Beim Erstellen des Authentifizierungsschlüssels ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
<de><![CDATA[Beim Erstellen des Authentifizierungs-Schlüssels ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
</locale>
<locale name="message.ajax.generateKey.success">
<de><![CDATA[Der Authentifizierungsschlüssel "[title]" wurde erfolgreich erstellt.]]></de>
<de><![CDATA[Der Authentifizierungs-Schlüssel "[title]" wurde erfolgreich erstellt.]]></de>
</locale>
<locale name="message.ajax.getKey.error" html="true">
<de><![CDATA[Beim Abruf des Authentifizierungs-Schlüssels ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
......@@ -39,10 +39,16 @@
<de><![CDATA[Beim Abruf der Authentifizierungs-Schlüssels ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
</locale>
<locale name="message.ajax.deleteKeys.error" html="true">
<de><![CDATA[Beim Löschen der Authentifizierungsschlüssels ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
<de><![CDATA[Beim Löschen der Authentifizierungs-Schlüssel ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
</locale>
<locale name="message.ajax.deleteKeys.success">
<de><![CDATA[Die gewählten Authentifizierungsschlüssel wurden erfolgreich gelöscht.]]></de>
<de><![CDATA[Die gewählten Authentifizierungs-Schlüssel wurden erfolgreich gelöscht.]]></de>
</locale>
<locale name="message.ajax.regenerateRecoveryKeys.error" html="true">
<de><![CDATA[Beim Neu-Generieren der Einmal-Login-Codes ist ein Fehler aufgetreten:<br/><br/>[error]]]></de>
</locale>
<locale name="message.ajax.regenerateRecoveryKeys.success">
<de><![CDATA[Die Einmal-Login-Codes wurden neu generiert. Die bisherigen Codes sind ab jetzt nicht mehr gültig.]]></de>
</locale>
<!-- Class: Auth -->
......@@ -58,7 +64,7 @@
<!-- Login -->
<locale name="login.code">
<de><![CDATA[Authentifizierungs-Code]]></de>
<de><![CDATA[Google Authenticator Code]]></de>
</locale>
<locale name="login.btn.auth">
<de><![CDATA[Authentifizieren]]></de>
......@@ -70,7 +76,7 @@
<!-- Control: Settings -->
<locale name="controls.settings.generatekey.title">
<de><![CDATA[Neuer Authentifizierungsschlüssel]]></de>
<de><![CDATA[Neuer Authentifizierungs-Schlüssel]]></de>
</locale>
<locale name="controls.settings.generatekey.title.label">
<de><![CDATA[Bezeichnung]]></de>
......@@ -103,7 +109,7 @@
<de><![CDATA[Erstellungsdatum]]></de>
</locale>
<locale name="controls.settings.showkey.template.labelRecoveryKeys">
<de><![CDATA[Einmal-Login-Schlüssel]]></de>
<de><![CDATA[Einmal-Login-Codes]]></de>
</locale>
<locale name="controls.settings.deleteKeys.title">
<de><![CDATA[Authentifizierungs-Schlüssel löschen]]></de>
......@@ -111,5 +117,15 @@
<locale name="controls.settings.deleteKeys.info" html="true">
<de><![CDATA[Sind Sie sicher, dass Sie die folgenden Authentifizierungs-Schlüssel unwiderruflich löschen wollen? Alle mit diesen Schlüsseln verknüpften Geräte können sich danach nicht mehr mit generierten Codes zu diesen Schlüsseln authentifizieren.<br/><br/>[titles]]]></de>
</locale>
<locale name="controls.settings.showkey.regenerate.recoverykeys.btn">
<de><![CDATA[Neu generieren]]></de>
</locale>
<locale name="controls.settings.showkey.regenerate.warning.title">
<de><![CDATA[Einmal-Login-Codes neu generieren]]></de>
</locale>
<locale name="controls.settings.showkey.regenerate.warning">
<de><![CDATA[Sind Sie sicher, dass Sie die Einmal-Login-Codes neu generieren wollen? Alle alten Codes sind danach nicht mehr gültig und es können nur die neuen Codes verwendet werden.]]></de>
</locale>
</groups>
</locales>
\ No newline at end of file
......@@ -92,19 +92,26 @@ public function auth($authData)
}
// if key did not work check for recovery keys
foreach ($secretData['recoveryKeys'] as $k2 => $recoveryKey) {
$recoveryKey = trim(Security::decrypt($recoveryKey));
foreach ($secretData['recoveryKeys'] as $k2 => $recoveryKeyData) {
if ($recoveryKeyData['used']) {
continue;
}
$recoveryKey = trim(Security::decrypt($recoveryKeyData['key']));
if ($recoveryKey != $authCode) {
continue;
}
// remove recovery key from list indefinitely
unset($secretData['recoveryKeys'][$k2]);
$authSecrets[$k] = $secretData;
// set used status of recovery key to true
$recoveryKeyData['used'] = true;
$recoveryKeyData['usedDate'] = date('Y-m-d H:i:s');
$secretData['recoveryKeys'][$k2] = $recoveryKeyData;
$authSecrets[$k] = $secretData;
$this->User->setAttribute('quiqqer.auth.google2fa.secrets', json_encode($authSecrets));
$this->User->save();
$this->User->save(QUI::getUsers()->getSystemUser());
return;
}
......@@ -148,7 +155,11 @@ public static function generateRecoveryKeys($count = 10)
$Google2FA = new Google2FA();
for ($i = 0; $i < $count; $i++) {
$recoveryKeys[] = Security::encrypt(mb_substr(md5($Google2FA->generateSecretKey(16)), 0, 10));
$recoveryKeys[] = array(
'key' => Security::encrypt(md5($Google2FA->generateSecretKey(16))),
'used' => false,
'usedDate' => false
);
}
return $recoveryKeys;
......
......@@ -16,6 +16,11 @@
width: 100%;
}
.quiqqer-auth-google2fa-login input {
display: block;
width: 100%;
}
.quiqqer-auth-google2fa-login [type="submit"] {
cursor: pointer;
}
\ No newline at end of file
......@@ -20,6 +20,8 @@ class Login extends Control
*/
public function getBody()
{
$this->addCSSFile(dirname(__FILE__) . '/Login.css');
$Engine = QUI::getTemplateManager()->getEngine();
return $Engine->fetch(dirname(__FILE__) . '/Login.html');
}
......
......@@ -2,7 +2,7 @@
namespace QUI\Auth\Google2Fa;
use QUI\Exception as QUIExcpetion;
use QUI\Users\Exception as QUIExcpetion;
class Exception extends QUIExcpetion
{
......
0% oder .
You are about to add 0 people to the discussion. Proceed with caution.
Bearbeitung dieser Nachricht zuerst beenden!
Bitte registrieren oder zum Kommentieren