add otp security check to critical settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
@@ -107,7 +107,8 @@ return [
|
||||
'varname' => 'enabled',
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'api_customer_default' => [
|
||||
'label' => lng('serversettings.api_customer_default'),
|
||||
|
||||
@@ -46,7 +46,8 @@ return [
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||
'default' => '/usr/bin/nice -n 5 /usr/bin/php -q',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_crondreload' => [
|
||||
'label' => lng('serversettings.system_crondreload'),
|
||||
@@ -55,7 +56,8 @@ return [
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||
'default' => '/etc/init.d/cron reload',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_cron_allowautoupdate' => [
|
||||
'label' => lng('serversettings.system_cron_allowautoupdate'),
|
||||
@@ -63,7 +65,8 @@ return [
|
||||
'varname' => 'cron_allowautoupdate',
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -308,7 +308,8 @@ return [
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||
'default' => '/etc/init.d/apache2 reload',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_phpreload_command' => [
|
||||
'label' => lng('serversettings.phpreload_command'),
|
||||
@@ -320,7 +321,8 @@ return [
|
||||
'save_method' => 'storeSettingField',
|
||||
'websrv_avail' => [
|
||||
'nginx'
|
||||
]
|
||||
],
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_nginx_php_backend' => [
|
||||
'label' => lng('serversettings.nginx_php_backend'),
|
||||
|
||||
@@ -157,7 +157,8 @@ return [
|
||||
'string_type' => 'file',
|
||||
'default' => '/root/.acme.sh/acme.sh',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_letsencryptacmeconf' => [
|
||||
'label' => lng('serversettings.letsencryptacmeconf'),
|
||||
|
||||
@@ -126,7 +126,8 @@ return [
|
||||
'type' => 'textarea',
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'phpfpm_ini_values' => [
|
||||
'label' => lng('phpfpm.ini_values'),
|
||||
@@ -135,7 +136,8 @@ return [
|
||||
'type' => 'textarea',
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'phpfpm_ini_admin_flags' => [
|
||||
'label' => lng('phpfpm.ini_admin_flags'),
|
||||
@@ -144,7 +146,8 @@ return [
|
||||
'type' => 'textarea',
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'phpfpm_ini_admin_values' => [
|
||||
'label' => lng('phpfpm.ini_admin_values'),
|
||||
@@ -153,7 +156,8 @@ return [
|
||||
'type' => 'textarea',
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -80,7 +80,8 @@ return [
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||
'default' => '/etc/init.d/bind9 reload',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_nameservers' => [
|
||||
'label' => lng('serversettings.nameservers'),
|
||||
@@ -111,7 +112,8 @@ return [
|
||||
'string_delimiter' => ',',
|
||||
'string_emptyallowed' => true,
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_powerdns_mode' => [
|
||||
'label' => lng('serversettings.powerdns_mode'),
|
||||
|
||||
@@ -137,7 +137,8 @@ return [
|
||||
'type' => 'text',
|
||||
'string_regexp' => '/^[a-z0-9\/\._\- ]+$/i',
|
||||
'default' => '/etc/init.d/dkim-filter restart',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -37,7 +37,8 @@ return [
|
||||
'varname' => 'unix_names',
|
||||
'type' => 'checkbox',
|
||||
'default' => true,
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_mailpwcleartext' => [
|
||||
'label' => lng('serversettings.mailpwcleartext'),
|
||||
@@ -46,7 +47,8 @@ return [
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_passwordcryptfunc' => [
|
||||
'label' => lng('serversettings.passwordcryptfunc'),
|
||||
@@ -59,7 +61,8 @@ return [
|
||||
'getAvailablePasswordHashes'
|
||||
],
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_allow_error_report_admin' => [
|
||||
'label' => lng('serversettings.allow_error_report_admin'),
|
||||
@@ -67,7 +70,8 @@ return [
|
||||
'varname' => 'allow_error_report_admin',
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_allow_error_report_customer' => [
|
||||
'label' => lng('serversettings.allow_error_report_customer'),
|
||||
@@ -75,7 +79,8 @@ return [
|
||||
'varname' => 'allow_error_report_customer',
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_allow_customer_shell' => [
|
||||
'label' => lng('serversettings.allow_allow_customer_shell'),
|
||||
@@ -84,7 +89,8 @@ return [
|
||||
'type' => 'checkbox',
|
||||
'default' => false,
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_available_shells' => [
|
||||
'label' => lng('serversettings.available_shells'),
|
||||
@@ -94,7 +100,8 @@ return [
|
||||
'string_emptyallowed' => true,
|
||||
'default' => '',
|
||||
'save_method' => 'storeSettingField',
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_froxlorusergroup' => [
|
||||
'label' => lng('serversettings.froxlorusergroup'),
|
||||
@@ -108,7 +115,8 @@ return [
|
||||
'checkLocalGroup'
|
||||
],
|
||||
'visible' => Settings::Get('system.nssextrausers'),
|
||||
'advanced_mode' => true
|
||||
'advanced_mode' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
@@ -44,24 +44,30 @@ return [
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_repquota_path',
|
||||
'type' => 'text',
|
||||
'string_type' => 'file',
|
||||
'default' => '/usr/sbin/repquota',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_diskquota_quotatool_path' => [
|
||||
'label' => lng('serversettings.diskquota_quotatool_path.description'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_quotatool_path',
|
||||
'type' => 'text',
|
||||
'string_type' => 'file',
|
||||
'default' => '/usr/bin/quotatool',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
],
|
||||
'system_diskquota_customer_partition' => [
|
||||
'label' => lng('serversettings.diskquota_customer_partition.description'),
|
||||
'settinggroup' => 'system',
|
||||
'varname' => 'diskquota_customer_partition',
|
||||
'type' => 'text',
|
||||
'string_type' => 'file',
|
||||
'default' => '/dev/root',
|
||||
'save_method' => 'storeSettingField'
|
||||
'save_method' => 'storeSettingField',
|
||||
'required_otp' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -77,7 +77,8 @@ if ($page == 'overview' && $userinfo['change_serversettings'] == '1') {
|
||||
if (Form::processForm($settings_data, $_POST, [
|
||||
'filename' => $filename,
|
||||
'action' => $action,
|
||||
'page' => $page
|
||||
'page' => $page,
|
||||
'part' => $_part,
|
||||
], $_part, $settings_all, $settings_part, $only_enabledisable)) {
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "rebuild configfiles due to changed setting");
|
||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||
|
||||
@@ -25,10 +25,13 @@
|
||||
|
||||
namespace Froxlor;
|
||||
|
||||
use Exception;
|
||||
use Froxlor\Api\Commands\Customers;
|
||||
use Froxlor\Api\Commands\SubDomains;
|
||||
use Froxlor\Database\Database;
|
||||
use Froxlor\UI\Collection;
|
||||
use Froxlor\UI\Response;
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
|
||||
/**
|
||||
* Class to manage the current user / session
|
||||
@@ -169,4 +172,65 @@ class CurrentUser
|
||||
|
||||
return ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TwoFactorAuthException
|
||||
*/
|
||||
public static function sendOtpEmail()
|
||||
{
|
||||
global $mail;
|
||||
|
||||
if (self::getField('type_2fa') == 1) {
|
||||
// generate code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$code = $tfa->getCode($tfa->createSecret());
|
||||
// set code for user
|
||||
$table = TABLE_PANEL_CUSTOMERS;
|
||||
$uid = 'customerid';
|
||||
if (self::isAdmin()) {
|
||||
$table = TABLE_PANEL_ADMINS;
|
||||
$uid = 'adminid';
|
||||
}
|
||||
$stmt = Database::prepare("UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid");
|
||||
Database::pexecute($stmt, [
|
||||
"d2fa" => $code,
|
||||
"uid" => self::getField($uid)
|
||||
]);
|
||||
// build up & send email
|
||||
$_mailerror = false;
|
||||
$mailerr_msg = "";
|
||||
$replace_arr = [
|
||||
'CODE' => $code
|
||||
];
|
||||
$mail_body = html_entity_decode(PhpHelper::replaceVariables(lng('mails.2fa.mailbody'), $replace_arr));
|
||||
|
||||
try {
|
||||
$mail->Subject = lng('mails.2fa.subject');
|
||||
$mail->AltBody = $mail_body;
|
||||
$mail->MsgHTML(str_replace("\n", "<br />", $mail_body));
|
||||
$mail->AddAddress(self::getField('email'), User::getCorrectUserSalutation(self::getData()));
|
||||
$mail->Send();
|
||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||
$mailerr_msg = $e->errorMessage();
|
||||
$_mailerror = true;
|
||||
} catch (Exception $e) {
|
||||
$mailerr_msg = $e->getMessage();
|
||||
$_mailerror = true;
|
||||
}
|
||||
|
||||
if ($_mailerror) {
|
||||
$rstlog = FroxlorLogger::getInstanceOf([
|
||||
'loginname' => '2fa code-sending'
|
||||
]);
|
||||
$rstlog->logAction(FroxlorLogger::ADM_ACTION, LOG_ERR, "Error sending mail: " . $mailerr_msg);
|
||||
Response::redirectTo('index.php', [
|
||||
'showmessage' => '4',
|
||||
'customermail' => self::getField('email')
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$mail->ClearAddresses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,8 @@ class Settings
|
||||
{
|
||||
// set defaults
|
||||
self::$conf = [
|
||||
'enable_webupdate' => false
|
||||
'enable_webupdate' => false,
|
||||
'disable_otp_security_check' => false,
|
||||
];
|
||||
|
||||
$configfile = Froxlor::getInstallDir() . '/lib/config.inc.php';
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace Froxlor\UI;
|
||||
|
||||
use Froxlor\CurrentUser;
|
||||
use Froxlor\FroxlorTwoFactorAuth;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\Validate\Check;
|
||||
|
||||
@@ -183,6 +185,21 @@ class Form
|
||||
}
|
||||
}
|
||||
|
||||
// OTP security validation for sensitive settings
|
||||
if (!Settings::Config('disable_otp_security_check') && isset($fielddata['required_otp']) && $do_show) {
|
||||
$otp_enabled_system = (bool)Settings::Get('2fa.enabled');
|
||||
$otp_enabled_user = (int)CurrentUser::getField('type_2fa') != 0;
|
||||
$do_show = !$fielddata['required_otp'] || ($otp_enabled_system && $otp_enabled_user);
|
||||
if (!$do_show) {
|
||||
$fielddata['note'] = lng('serversettings.option_required_otp');
|
||||
if (!$otp_enabled_system) {
|
||||
$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated');
|
||||
} elseif (!$otp_enabled_user) {
|
||||
$fielddata['note'] .= '<br>' . lng('2fa.2fa_not_activated_for_user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$do_show) {
|
||||
$fielddata['visible'] = false;
|
||||
}
|
||||
@@ -283,6 +300,38 @@ class Form
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Settings::Config('disable_otp_security_check') && isset($fielddetails['required_otp']) && isset($changed_fields[$fieldname])) {
|
||||
$otp_enabled_system = (bool)Settings::Get('2fa.enabled');
|
||||
$otp_enabled_user = (int)CurrentUser::getField('type_2fa') != 0;
|
||||
$do_update = !$fielddetails['required_otp'] || ($otp_enabled_system && $otp_enabled_user);
|
||||
if ($do_update) {
|
||||
// setting that requires OTP verification
|
||||
if (empty($input['otp_verification'])) {
|
||||
// in case email 2fa is enabled, send it now
|
||||
CurrentUser::sendOtpEmail();
|
||||
// build up form
|
||||
if (is_array($url_params) && isset($url_params['filename'])) {
|
||||
$filename = $url_params['filename'];
|
||||
unset($url_params['filename']);
|
||||
} else {
|
||||
$filename = '';
|
||||
}
|
||||
HTML::askOTP('please_enter_otp', $filename, array_merge($url_params, $submitted_fields));
|
||||
} else {
|
||||
// validate given OTP code
|
||||
$code = trim($input['otp_verification']);
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$result = $tfa->verifyCode(CurrentUser::getField('data_2fa'), $code, 3);
|
||||
if (!$result) {
|
||||
Response::standardError('otpnotvalidated');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// do not update this setting
|
||||
unset($changed_fields[$fieldname]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,4 +221,17 @@ class HTML
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
public static function askOTP(string $text, string $targetfile, array $params = [], string $replacer = '', array $back_link = [])
|
||||
{
|
||||
$text = lng('question.' . $text, [htmlspecialchars($replacer)]);
|
||||
|
||||
Panel\UI::view('form/otpquestion.html.twig', [
|
||||
'action' => $targetfile,
|
||||
'url_params' => $params,
|
||||
'question' => $text,
|
||||
'back_link' => $back_link
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,9 @@ return [
|
||||
* updates the way the providers does it (e.g. automation, etc.)
|
||||
*/
|
||||
'enable_webupdate' => false,
|
||||
|
||||
/**
|
||||
* @todo description
|
||||
*/
|
||||
'disable_otp_security_check' => false,
|
||||
];
|
||||
|
||||
@@ -44,7 +44,8 @@ return [
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => 'service php7.4-fpm restart',
|
||||
'mandatory' => true
|
||||
'mandatory' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'config_dir' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.configdir'),
|
||||
|
||||
@@ -45,7 +45,8 @@ return [
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => $result['reload_cmd'],
|
||||
'mandatory' => true
|
||||
'mandatory' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'config_dir' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.configdir'),
|
||||
|
||||
@@ -46,7 +46,8 @@ return [
|
||||
'label' => lng('admin.phpsettings.binary'),
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => '/usr/bin/php-cgi'
|
||||
'value' => '/usr/bin/php-cgi',
|
||||
'required_otp' => true
|
||||
],
|
||||
'fpmconfig' => [
|
||||
'visible' => Settings::Get('phpfpm.enabled') == 1,
|
||||
@@ -61,7 +62,8 @@ return [
|
||||
'desc' => lng('admin.phpsettings.file_extensions_note'),
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => 'php'
|
||||
'value' => 'php',
|
||||
'required_otp' => true
|
||||
],
|
||||
'mod_fcgid_starter' => [
|
||||
'visible' => Settings::Get('system.mod_fcgid') == 1,
|
||||
@@ -181,7 +183,8 @@ return [
|
||||
'cols' => 80,
|
||||
'rows' => 20,
|
||||
'value' => $result['phpsettings'],
|
||||
'mandatory' => true
|
||||
'mandatory' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'allow_all_customers' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.allow_all_customers.title'),
|
||||
|
||||
@@ -47,7 +47,8 @@ return [
|
||||
'label' => lng('admin.phpsettings.binary'),
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => $result['binary']
|
||||
'value' => $result['binary'],
|
||||
'required_otp' => true
|
||||
],
|
||||
'fpmconfig' => [
|
||||
'visible' => Settings::Get('phpfpm.enabled') == 1,
|
||||
@@ -62,7 +63,8 @@ return [
|
||||
'desc' => lng('admin.phpsettings.file_extensions_note'),
|
||||
'type' => 'text',
|
||||
'maxlength' => 255,
|
||||
'value' => $result['file_extensions']
|
||||
'value' => $result['file_extensions'],
|
||||
'required_otp' => true
|
||||
],
|
||||
'mod_fcgid_starter' => [
|
||||
'visible' => Settings::Get('system.mod_fcgid') == 1,
|
||||
@@ -185,7 +187,8 @@ return [
|
||||
'cols' => 80,
|
||||
'rows' => 20,
|
||||
'value' => $result['phpsettings'],
|
||||
'mandatory' => true
|
||||
'mandatory' => true,
|
||||
'required_otp' => true
|
||||
],
|
||||
'allow_all_customers' => [
|
||||
'label' => lng('serversettings.phpfpm_settings.allow_all_customers.title'),
|
||||
|
||||
31
templates/Froxlor/form/otpquestion.html.twig
Normal file
31
templates/Froxlor/form/otpquestion.html.twig
Normal file
@@ -0,0 +1,31 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form action="{{ action|default("") }}" method="post" enctype="application/x-www-form-urlencoded" class="form">
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('panel.security_question') }}</h4>
|
||||
<p>{{ question|raw }}</p>
|
||||
<p>
|
||||
<input name="otp_verification" id="otp_verification" type="text" class="form-control"
|
||||
placeholder="{{ lng('login.2facode') }}" autocomplete="off" autofocus required/>
|
||||
</p>
|
||||
<p>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
{% for id,field in url_params %}
|
||||
<input type="hidden" name="{{ id }}" value="{{ field }}"/>
|
||||
{% endfor %}
|
||||
<button class="btn btn-danger" type="submit" name="submitbutton">{{ lng('2fa.2fa_verify') }}</button>
|
||||
{% if back_link is defined and back_link is iterable and back_link|length > 0 %}
|
||||
<a href="{{ linker(back_link) }}" class="btn btn-secondary">{{ lng('panel.cancel') }}</a>
|
||||
{% else %}
|
||||
<a href="javascript:history.back(-1)" class="btn btn-secondary">{{ lng('panel.cancel') }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user