Skip to content

Commit

Permalink
Add setting to lock the configuration
Browse files Browse the repository at this point in the history
This will require a successful 2FA authentication to make any changes
to the configuration or disable 2FA altogether.
  • Loading branch information
bartnv committed May 31, 2023
1 parent c5333c1 commit 67bee8f
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 1 deletion.
3 changes: 3 additions & 0 deletions localization/en_US.inc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ $labels['key_checked'] = 'Successfully checked key';
$labels['request_key_name'] = 'Please enter a name for this key';
$labels['edit_key_name'] = 'Please enter a new name for key';
$labels['key_renamed'] = 'Key renamed to';
$labels['lock_config'] = 'Lock configuration';
$labels['lock_hint'] = 'Require authentication to change or disable 2FA.';

// Messages used for the different portions of the plugin
$messages = array();
$messages['successfully_saved'] = 'Settings saved successfully.';
$messages['error_locked'] = 'Action failed: configuration is locked.';
3 changes: 3 additions & 0 deletions localization/nl_NL.inc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ $labels['key_checked'] = 'Sleutel gecontroleerd met ID';
$labels['request_key_name'] = 'Voer een naam in voor deze sleutel';
$labels['edit_key_name'] = 'Voer een nieuwe naam in voor sleutel';
$labels['key_renamed'] = 'Sleutel hernoemd naar';
$labels['lock_config'] = 'Configuratie vergrendelen';
$labels['lock_hint'] = 'Vereist authenticatie om 2FA te wijzigen of uit te schakelen.';

// Messages used for the different portions of the plugin
$messages = array();
$messages['successfully_saved'] = 'Instellingen zijn succesvol opgeslagen.';
$messages['error_locked'] = 'Actie mislukt: configuratie is vergrendeld.';
8 changes: 7 additions & 1 deletion twofactor_webauthn.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ if (window.rcmail) {
rcmail.addEventListener('plugin.twofactor_webauthn_list', twofactor_webauthn_list);
if (rcmail.env.twofactor_webauthn_keylist) twofactor_webauthn_list(JSON.parse(rcmail.env.twofactor_webauthn_keylist));
else rcmail.http_get('plugin.twofactor_webauthn_list');
$('#twofactor_activate').on('change', function() {
if (!this.checked) $('#twofactor_lock').prop('checked', false);
});
});
}

Expand All @@ -25,7 +28,10 @@ function twofactor_webauthn_test() {
rcmail.http_post('plugin.twofactor_webauthn_test');
}
function twofactor_webauthn_save() {
rcmail.http_post('plugin.twofactor_webauthn_save', { activate: $('#twofactor_activate').prop('checked') });
rcmail.http_post('plugin.twofactor_webauthn_save', {
activate: $('#twofactor_activate').prop('checked'),
lock: $('#twofactor_lock').prop('checked')
});
}

function twofactor_webauthn_challenge(data) {
Expand Down
52 changes: 52 additions & 0 deletions twofactor_webauthn.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,44 @@ function twofactor_webauthn_list() {
function twofactor_webauthn_save() {
$rcmail = rcmail::get_instance();
$activate = rcube_utils::get_input_value('activate', rcube_utils::INPUT_POST);
$lock = rcube_utils::get_input_value('lock', rcube_utils::INPUT_POST);
if ($activate === 'true') $activate = true;
elseif ($activate === 'false') $activate = false;
else {
error_log('Received invalid response on webauthn save');
return;
}
if ($lock === 'true') $lock = true;
elseif ($lock === 'false') $lock = false;
else {
error_log('Received invalid response on webauthn save');
return;
}
$config = $this->getConfig();
if (isset($config['lock']) && $config['lock'] === true) {
$webauthn = new \Davidearl\WebAuthn\WebAuthn($_SERVER['HTTP_HOST']);
$challenge = $webauthn->prepareForLogin($config['keys']);
if ($config['activate'] != $activate) $config['setactivate'] = $activate;
if ($config['lock'] != $lock) $config['setlock'] = $lock;
if (isset($config['setactivate']) || isset($config['setlock'])) {
$this->saveConfig($config);
$rcmail->output->command('plugin.twofactor_webauthn_challenge', [ 'mode' => 'test', 'challenge' => $challenge ]);
return;
}
}
$config['activate'] = $activate;
$config['lock'] = $lock;
$this->saveConfig($config);
$rcmail->output->show_message($this->gettext('successfully_saved'), 'confirmation');
}

function twofactor_webauthn_prepare() {
$rcmail = rcmail::get_instance();
$config = $this->getConfig();
if (isset($config['lock']) && $config['lock'] === true) {
$rcmail->output->show_message($this->gettext('error_locked'), 'error');
return;
}
$webauthn = new \Davidearl\WebAuthn\WebAuthn($_SERVER['HTTP_HOST']);
$challenge = $webauthn->prepareChallengeForRegistration('RoundCube', '1', true);
$rcmail->output->command('plugin.twofactor_webauthn_challenge', [ 'mode' => 'register', 'challenge' => $challenge ]);
Expand All @@ -134,6 +158,19 @@ function twofactor_webauthn_check() {
$webauthn = new \Davidearl\WebAuthn\WebAuthn($_SERVER['HTTP_HOST']);
$config = $this->getConfig();
if ($webauthn->authenticate($response, $config['keys'])) {
if (isset($config['setactivate']) || isset($config['setlock'])) {
if (isset($config['setactivate'])) {
$config['activate'] = $config['setactivate'];
unset($config['setactivate']);
}
if (isset($config['setlock'])) {
$config['lock'] = $config['setlock'];
unset($config['setlock']);
}
$this->saveConfig($config);
$rcmail->output->show_message($this->gettext('successfully_saved'), 'confirmation');
return;
}
$this->saveConfig($config);
$response = json_decode($response);
$rcmail->output->show_message($this->gettext('key_checked') . ' ' . dechex(crc32(implode('', $response->rawId))), 'confirmation');
Expand Down Expand Up @@ -172,6 +209,10 @@ function twofactor_webauthn_rename() {
}
$rcmail = rcmail::get_instance();
$config = $this->getConfig();
if (isset($config['lock']) && $config['lock'] === true) {
$rcmail->output->show_message($this->gettext('error_locked'), 'error');
return;
}
$keys = json_decode($config['keys']);
foreach ($keys as &$key) {
if (dechex(crc32(implode('', $key->id))) === $id) {
Expand All @@ -192,6 +233,10 @@ function twofactor_webauthn_delete() {
}
$rcmail = rcmail::get_instance();
$config = $this->getConfig();
if (isset($config['lock']) && $config['lock'] === true) {
$rcmail->output->show_message($this->gettext('error_locked'), 'error');
return;
}
$newkeys = [];
foreach (json_decode($config['keys']) as $key) {
if (dechex(crc32(implode('', $key->id))) === $id) continue;
Expand Down Expand Up @@ -252,6 +297,12 @@ public function twofactor_webauthn_form() {
$table->add('title', html::label($field_id, rcube::Q($this->gettext('activate'))));
$table->add(null, $checkbox_activate->show($config['activate']==true?false:true));

$field_id = 'twofactor_lock';
$checkbox_lock = new html_checkbox([ 'name' => $field_id, 'id' => $field_id, 'type' => 'checkbox' ]);
$hint = html::tag('div', [ 'class' => 'hint', 'style' => 'margin-top: -0.5rem' ], rcube::Q($this->gettext('lock_hint')));
$table->add('title', html::label($field_id, rcube::Q($this->gettext('lock_config'))) . $hint);
$table->add(null, $checkbox_lock->show($config['lock']==true?false:true));

$rcmail->output->add_gui_object('webauthnform', 'twofactor_webauthn-form');
$form = $rcmail->output->form_tag([
'id' => 'twofactor_webauthn-form',
Expand Down Expand Up @@ -282,6 +333,7 @@ private function getConfig() {
$prefs = $rcmail->user->get_prefs();
$config = $prefs['twofactor_webauthn'] ?? [];
if (!isset($config['activate'])) $config['activate'] = false;
if (!isset($config['lock'])) $config['lock'] = false;
if (!isset($config['keys'])) $config['keys'] = '[]';
return $config;
}
Expand Down

0 comments on commit 67bee8f

Please sign in to comment.