Add Radius second factor (#1847)

This commit is contained in:
Maxime Besson 2019-07-12 11:53:22 +02:00
parent 68c8be333a
commit 15c3b0bbf8
27 changed files with 286 additions and 7 deletions

View File

@ -17,7 +17,7 @@ sub defaultValues {
},
'authChoiceParam' => 'lmAuth',
'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3,
@ -218,6 +218,8 @@ sub defaultValues {
'portalSkin' => 'bootstrap',
'portalUserAttr' => '_user',
'proxyAuthnLevel' => 2,
'radius2fActivation' => 0,
'radius2fTimeout' => 20,
'radiusAuthnLevel' => 3,
'randomPasswordRegexp' => '[A-Z]{3}[a-z]{5}.\\d{2}',
'redirectFormMethod' => 'get',

View File

@ -598,7 +598,7 @@ sub attributes {
'type' => 'keyTextContainer'
},
'available2F' => {
'default' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey',
'default' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'type' => 'text'
},
'available2FSelfRegistration' => {
@ -2419,6 +2419,32 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 0,
'type' => 'bool'
},
'radius2fActivation' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'radius2fAuthnLevel' => {
'type' => 'int'
},
'radius2fLabel' => {
'type' => 'text'
},
'radius2fLogo' => {
'type' => 'text'
},
'radius2fSecret' => {
'type' => 'text'
},
'radius2fServer' => {
'type' => 'text'
},
'radius2fTimeout' => {
'default' => 20,
'type' => 'int'
},
'radius2fUsernameSessionKey' => {
'type' => 'text'
},
'radiusAuthnLevel' => {
'default' => 3,
'type' => 'int'
@ -3250,6 +3276,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
{
'k' => 'Ext2F',
'v' => 'External'
},
{
'k' => 'Radius',
'v' => 'Radius'
}
],
'test' => sub {

View File

@ -1592,6 +1592,37 @@ sub attributes {
documentation => 'Custom logo for External 2F',
},
# Radius second factor
radius2fActivation => {
type => 'boolOrExpr',
default => 0,
documentation => 'Radius second factor activation',
},
radius2fSecret => { type => 'text', },
radius2fServer => { type => 'text', },
radius2fUsernameSessionKey => {
type => 'text',
documentation => 'Session key used as Radius login'
},
radius2fTimeout => {
type => 'int',
default => 20,
documentation => 'Radius 2f verification timeout',
},
radius2fAuthnLevel => {
type => 'int',
documentation =>
'Authentication level for users authenticated by Radius second factor'
},
radius2fLogo => {
type => 'text',
documentation => 'Custom logo for Radius 2F',
},
radius2fLabel => {
type => 'text',
documentation => 'Portal label for Radius 2F'
},
# REST External second factor
rest2fActivation => {
type => 'boolOrExpr',
@ -2709,7 +2740,7 @@ sub attributes {
},
available2F => {
type => 'text',
default => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey',
default => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
documentation => 'Available second factor modules',
},
available2FSelfRegistration => {
@ -3353,6 +3384,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
{ k => 'Mail2F', v => 'E-Mail' },
{ k => 'REST', v => 'REST' },
{ k => 'Ext2F', v => 'External' },
{ k => 'Radius', v => 'Radius' },
],
},

View File

@ -758,6 +758,21 @@ sub tree {
'ext2fLogo',
]
},
{
title => 'radius2f',
help => 'radius2f.html',
form => 'simpleInputContainer',
nodes => [
'radius2fActivation',
'radius2fServer',
'radius2fSecret',
'radius2fUsernameSessionKey',
'radius2fTimeout',
'radius2fAuthnLevel',
'radius2fLogo',
'radius2fLabel',
]
},
{
title => 'rest2f',
help => 'rest2f.html',

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"استخدام سواب بدلا من ريست",
"publicKey":"المفتاح العمومي",
"purgeNotification":"حذف الإشعار نهائيا",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"مستوى إثبات الهوية",
"radiusParams":"معايير راديوس",
"radiusSecret":"سر مشترك",

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Use SOAP instead of REST",
"publicKey":"Public key",
"purgeNotification":"Delete notification definitely",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Authentication level",
"radiusParams":"Radius parameters",
"radiusSecret":"Shared secret",

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Use SOAP instead of REST",
"publicKey":"Public key",
"purgeNotification":"Delete notification definitely",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Authentication level",
"radiusParams":"Radius parameters",
"radiusSecret":"Shared secret",
@ -1035,5 +1044,5 @@
"samlCommonDomainCookieWriter":"Writer URL",
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Utiliser SOAP au lieu de REST",
"publicKey":"Clef publique",
"purgeNotification":"Supprimer définitivement la notification",
"radius2f":"Second facteur Radius",
"radius2fActivation":"Activation",
"radius2fServer":"Nom d'hôte du serveur",
"radius2fSecret":"Secret partagé",
"radius2fUsernameSessionKey":"Clé de session contenant le login",
"radius2fTimeout":"Délai maximum d'authentification",
"radius2fAuthnLevel":"Niveau d'authentification",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Niveau d'authentification",
"radiusParams":"Paramètres Radius",
"radiusSecret":"Secret partagé",
@ -1035,5 +1044,5 @@
"samlCommonDomainCookieWriter":"URL d'écriture",
"samlRelayStateTimeout":"Durée de vie d'une session RelayState",
"samlUseQueryStringSpecific":"Utilisation d'une fonction spécifique pour query_string",
"samlOverrideIDPEntityID": "Valeur de l'Entity ID en mode IDP"
"samlOverrideIDPEntityID":"Valeur de l'Entity ID en mode IDP"
}

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Usa SOAP invece di REST",
"publicKey":"Chiave pubblica",
"purgeNotification":"Elimina definitivamente la notifica",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Livello di autenticazione",
"radiusParams":"Parametri di Radius",
"radiusSecret":"Segreto condiviso",

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Sử dụng SOAP thay vì REST",
"publicKey":"Khóa công khai",
"purgeNotification":"Xóa thông báo chắc chắn",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Mức xác thực",
"radiusParams":"Tham số Radius",
"radiusSecret":"Bí mật đã được chia sẻ",

View File

@ -674,6 +674,15 @@
"proxyUseSoap":"Use SOAP instead of REST",
"publicKey":"Public key",
"purgeNotification":"Delete notification definitely",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"认证等级",
"radiusParams":"Radius parameters",
"radiusSecret":"Shared secret",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,7 @@ lib/Lemonldap/NG/Portal/2F/Mail2F.pm
lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm
lib/Lemonldap/NG/Portal/2F/Register/U2F.pm
lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm
lib/Lemonldap/NG/Portal/2F/Radius.pm
lib/Lemonldap/NG/Portal/2F/REST.pm
lib/Lemonldap/NG/Portal/2F/TOTP.pm
lib/Lemonldap/NG/Portal/2F/U2F.pm

View File

@ -0,0 +1,112 @@
package Lemonldap::NG::Portal::2F::Radius;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_ERROR
PE_MALFORMEDUSER
PE_OK
PE_SENDRESPONSE
);
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
# INITIALIZATION
has prefix => ( is => 'rw', default => 'radius' );
has radius => ( is => 'rw' );
sub init {
my ($self) = @_;
foreach (qw(radius2fSecret radius2fServer)) {
unless ( $self->conf->{$_} ) {
$self->error("Missing $_ parameter, aborting");
return 0;
}
}
eval { require Authen::Radius };
if ($@) {
$self->error("Unable to load Authen::Radius: $@");
return 0;
}
unless (
$self->radius(
Authen::Radius->new(
Host => $self->conf->{radius2fServer},
Secret => $self->conf->{radius2fSecret},
TimeOut => $self->conf->{radius2fTimeout},
)
)
)
{
$self->error('Radius connect failed');
}
$self->prefix( $self->conf->{sfPrefix} )
if ( $self->conf->{sfPrefix} );
return $self->SUPER::init();
}
sub run {
my ( $self, $req, $token ) = @_;
my $checkLogins = $req->param('checkLogins');
$self->logger->debug("Radius2F checkLogins set") if ($checkLogins);
# Prepare form
my $tmp = $self->p->sendHtml(
$req,
'ext2fcheck',
params => {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
TARGET => '/' . $self->prefix . '2fcheck',
LEGEND => 'enterRadius2fCode',
CHECKLOGINS => $checkLogins
}
);
$self->logger->debug("Prepare Radius 2F verification");
$req->response($tmp);
return PE_SENDRESPONSE;
}
sub verify {
my ( $self, $req, $session ) = @_;
# Some Radius Servers allow empty codes and perform
# out of band, interactive verification (InWebo...)
my $code = $req->param('code');
# Launch Radius request
my $userAttr =
$self->conf->{radius2fUsernameSessionKey} || $self->conf->{whatToTrace};
my $username = $session->{$userAttr};
unless ($username) {
$self->logger->error(
"Could not find Radius username from session attribute $userAttr");
return PE_MALFORMEDUSER;
}
$self->logger->debug("Checking Radius credentials $username:$code");
my $res = $self->radius->check_pwd( $username, $code );
unless ( $res == 1 ) {
$self->userLogger->warn( "Radius second factor failed for "
. $session->{ $self->conf->{whatToTrace} } );
$self->logger->warn(
"Radius server replied: " . $self->radius->get_error );
return PE_BADCREDENTIALS;
}
$self->logger->debug("Radius server accepted 2F credentials");
PE_OK;
}
1;

View File

@ -128,6 +128,7 @@
"enterCred":"الرجاء إدخال بيانات الاعتماد الخاصة بك",
"enterExt2fCode":"تم إرسال رمز إليك. الرجاء إدخاله",
"enterOpenIDLogin":"الرجاء إدخال تسجيل الدخول الأوبين إيدي الخاص بك",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"يرجى استخدام يوبي كي الخاص بك",
"errorMsg":"رسالة خاطئة",
@ -195,6 +196,7 @@
"pwd":"كلمة المرور",
"pwdResetAlreadyIssued":"تم إصدار طلب إعادة تعيين كلمة المرور من قبل",
"pwdWillExpire":"٪ s من الأيام و٪ s من الساعات و٪ s من الدقائق و٪ s من الثواني قبل انتهاء صلاحية كلمة المرور، قم بتغييرها!",
"radius2f":"Radius",
"redirectedFrom":"تمت إعادة توجيهك من",
"redirectedIn":"ستتم إعادة توجيهك خلال 30 ثانية",
"redirectionInProgress":"جار إعادة التوجيه قيد التقدم ...",

View File

@ -128,6 +128,7 @@
"enterCred":"Bitte geben deine Zugangsdaten ein",
"enterExt2fCode":"Ein Code wurde an dich gesendet. Bitte gebe diesen ein",
"enterOpenIDLogin":"Bitte geben deinen OpenID-Login ein",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Gebe den TOTP Code ein",
"enterYubikey":"Benutze bitte deinen Yubikey",
"errorMsg":"Fehlermeldung",
@ -195,6 +196,7 @@
"pwd":"Passwort",
"pwdResetAlreadyIssued":"Eine Anfrage zum Zurücksetzen des Passworts wurde bereits gestellt",
"pwdWillExpire":"%s Tage, %s Stunden, %s Minuten und %s Sekunden bevor dein Passwort abläuft, bitte ändere es!",
"radius2f":"Radius",
"redirectedFrom":"Du wurdest umgeleitet von",
"redirectedIn":"Du wirst in 30 Sekunden umgeleitet",
"redirectionInProgress":"Umleitung läuft ...",

View File

@ -128,6 +128,7 @@
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%s days, %s hours, %s minutes and %s seconds before password expiration, change it!",
"radius2f":"Radius",
"redirectedFrom":"You were redirect from ",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%s days, %s hours, %s minutes and %s seconds before password expiration, change it!",
"radius2f":"Radius",
"redirectedFrom":"You were redirect from ",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Syötä käyttäjätietosi",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Virhe viesti",
@ -195,6 +196,7 @@
"pwd":"Salasana",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%d päivää, %d tuntia, %d minuuttia ja %sekunttia jäljellä salasanan vanhentumiseen, vaihda salasana!",
"radius2f":"Radius",
"redirectedFrom":"Olet uudelleenohjattu",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Merci de vous authentifier",
"enterExt2fCode":"Un code vous a été envoyé, entrez-le ici",
"enterOpenIDLogin":"Entrez votre identifiant OpenID",
"enterRadius2fCode":"Entrez votre code OTP",
"enterTotpCode":"Entrez le code TOTP",
"enterYubikey":"Utilisez votre Yubikey",
"errorMsg":"Message d'erreur",
@ -195,6 +196,7 @@
"pwd":"Mot de passe",
"pwdResetAlreadyIssued":"Une demande de réinitialisation de mot de passe a déjà été faite le ",
"pwdWillExpire":"%s jours, %s heures, %s minutes et %s secondes avant expiration de votre mot de passe, pensez à le changer !",
"radius2f":"Radius",
"redirectedFrom":"Vous avez été redirigé depuis ",
"redirectedIn":"Vous allez être redirigé(e) automatiquement dans 30 secondes",
"redirectionInProgress":"Redirection en cours ...",

View File

@ -128,6 +128,7 @@
"enterCred":"Inserisci le tue credenziali",
"enterExt2fCode":"Un codice vi é stato inviato. Inseritelo",
"enterOpenIDLogin":"Inserisci il tuo login OpenID",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Inserisci il codice TOTP",
"enterYubikey":"Utilizza il tuo Yubikey",
"errorMsg":"Messaggio di errore",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"Una richiesta di ripristino della password é già stata rilasciata",
"pwdWillExpire":"%s giorni, %s ore, %s minuti e %s secondi prima della scadenza della password, cambiala!",
"radius2f":"Radius",
"redirectedFrom":"Sei stato reindirizzato da",
"redirectedIn":"Sarai reindirizzato in 30 secondi",
"redirectionInProgress":"Reindirizzamento in corso ...",

View File

@ -128,6 +128,7 @@
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%s days, %s hours, %s minutes and %s seconds before password expiration, change it!",
"radius2f":"Radius",
"redirectedFrom":"You were redirect from ",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%s days, %s hours, %s minutes and %s seconds before password expiration, change it!",
"radius2f":"Radius",
"redirectedFrom":"You were redirect from ",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -195,6 +196,7 @@
"pwd":"Password",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"%s days, %s hours, %s minutes and %s seconds before password expiration, change it!",
"radius2f":"Radius",
"redirectedFrom":"You were redirect from ",
"redirectedIn":"You'll be redirected in 30 seconds",
"redirectionInProgress":"Redirection in progress...",

View File

@ -128,6 +128,7 @@
"enterCred":"Vui lòng nhập thông tin đăng nhập của bạn",
"enterExt2fCode":"Một mã đã được gửi cho bạn. Hãy nhập nó",
"enterOpenIDLogin":"Hãy nhập thông tin đăng nhập OpenID của bạn",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Vui lòng sử dụng Yubikey của bạn",
"errorMsg":"Thông báo lỗi",
@ -195,6 +196,7 @@
"pwd":"Mật khẩu",
"pwdResetAlreadyIssued":"Yêu cầu đặt lại mật khẩu đã được ban hành",
"pwdWillExpire":"%s ngày, %s giờ, %s phút và %s giây trước khi hết hạn mật khẩu, hãy thay đổi nó!",
"radius2f":"Radius",
"redirectedFrom":"Bạn đã được chuyển hướng từ",
"redirectedIn":"Bạn sẽ được chuyển hướng trong 30 giây",
"redirectionInProgress":"Đang tiến hành chuyển hướng...",

View File

@ -128,6 +128,7 @@
"enterCred":"请输入您的认证信息",
"enterExt2fCode":"验证法已发送,请输入",
"enterOpenIDLogin":"请输入您的 OpenID 认证",
"enterRadius2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"请使用您的Yubikey",
"errorMsg":"错误消息",
@ -195,6 +196,7 @@
"pwd":"密码",
"pwdResetAlreadyIssued":"A password reset request was already issued on ",
"pwdWillExpire":"距离密码失效还有 %d 天, %d 小时, %d 分钟, %d 秒, 请修改!",
"radius2f":"Radius",
"redirectedFrom":"您重定向自",
"redirectedIn":"您将30秒后重定向",
"redirectionInProgress":"重定向进行中",