Check if user can register one more device (#1386)
This commit is contained in:
parent
2134bfd366
commit
e2effb46a1
|
@ -27,7 +27,7 @@ sub types {
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -662,7 +662,7 @@ sub attributes {
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -1026,7 +1026,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval $s;
|
||||
my $err = join(
|
||||
|
@ -1111,7 +1111,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -1134,7 +1134,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -1489,7 +1489,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval $s;
|
||||
my $err = join(
|
||||
|
@ -1526,7 +1526,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -1885,7 +1885,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -2222,7 +2222,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -2925,7 +2925,7 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
|
||||
BEGIN {
|
||||
${^WARNING_BITS} =
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x01";
|
||||
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55\x05";
|
||||
}
|
||||
eval "$s $val";
|
||||
my $err = join(
|
||||
|
@ -3004,19 +3004,19 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'default' => 0,
|
||||
'select' => [
|
||||
{
|
||||
'k' => '0',
|
||||
'k' => 0,
|
||||
'v' => 'unsecuredCookie'
|
||||
},
|
||||
{
|
||||
'k' => '1',
|
||||
'k' => 1,
|
||||
'v' => 'securedCookie'
|
||||
},
|
||||
{
|
||||
'k' => '2',
|
||||
'k' => 2,
|
||||
'v' => 'doubleCookie'
|
||||
},
|
||||
{
|
||||
'k' => '3',
|
||||
'k' => 3,
|
||||
'v' => 'doubleCookieForSingleSession'
|
||||
}
|
||||
],
|
||||
|
|
|
@ -108,15 +108,14 @@ sub run {
|
|||
}
|
||||
|
||||
# Check if user can register one more device
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug(
|
||||
"Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size > $maxSize ) {
|
||||
$self->userLogger->error(
|
||||
"Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendError( $req, 'MaxNumberof2FDevicesReached', 200 );
|
||||
}
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error("Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||
400 );
|
||||
}
|
||||
|
||||
push @{$_2FDevices},
|
||||
{
|
||||
|
|
|
@ -30,6 +30,30 @@ sub run {
|
|||
my ( $self, $req, $action ) = @_;
|
||||
|
||||
if ( $action eq 'register' ) {
|
||||
|
||||
# Check if user can register one more device
|
||||
my $_2FDevices = eval {
|
||||
$self->logger->debug("Looking for 2F Devices ...");
|
||||
|
||||
# Read existing 2FDevices
|
||||
from_json( $req->userData->{_2FDevices}, { allow_nonref => 1 } );
|
||||
};
|
||||
unless ($_2FDevices) {
|
||||
$self->logger->debug("No 2F Device found");
|
||||
|
||||
# Set default value
|
||||
$_2FDevices = [];
|
||||
}
|
||||
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error("Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||
400 );
|
||||
}
|
||||
|
||||
my $challenge = $self->crypter->registrationChallenge;
|
||||
$self->logger->debug("Register challenge: $challenge");
|
||||
return [
|
||||
|
@ -77,17 +101,6 @@ sub run {
|
|||
$_2FDevices = [];
|
||||
}
|
||||
|
||||
# Check if user can register one more device
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug(
|
||||
"Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size > $maxSize ) {
|
||||
$self->userLogger->error(
|
||||
"Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendError( $req, 'MaxNumberof2FDevicesReached', 200 );
|
||||
}
|
||||
|
||||
my $keyName = $req->param('keyName');
|
||||
my $epoch = time();
|
||||
|
||||
|
|
|
@ -80,15 +80,20 @@ sub run {
|
|||
}
|
||||
|
||||
# Check if user can register one more device
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug(
|
||||
"Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size > $maxSize ) {
|
||||
$self->userLogger->error(
|
||||
"Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendError( $req, 'MaxNumberof2FDevicesReached', 200 );
|
||||
}
|
||||
my $size = @$_2FDevices;
|
||||
my $maxSize = $self->conf->{max2FDevices};
|
||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
||||
if ( $size >= $maxSize ) {
|
||||
$self->userLogger->error(
|
||||
"Max number of 2F devices is reached !!!");
|
||||
return $self->p->sendHtml(
|
||||
$req, 'error',
|
||||
params => {
|
||||
RAW_ERROR => 'maxNumberof2FDevicesReached',
|
||||
AUTH_ERROR_TYPE => 'warning',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
push @{$_2FDevices},
|
||||
{
|
||||
|
|
|
@ -38,7 +38,7 @@ register = ->
|
|||
$('#u2fPermission').hide()
|
||||
# Handle errors
|
||||
if data.errorCode
|
||||
setMsg 'unableToGetKey', 'warning'
|
||||
setMsg data.error, 'warning'
|
||||
else
|
||||
# 3 send response
|
||||
$.ajax
|
||||
|
|
|
@ -48,7 +48,7 @@ LemonLDAP::NG U2F registration script
|
|||
return u2f.register(ch.appId, request, [], function(data) {
|
||||
$('#u2fPermission').hide();
|
||||
if (data.errorCode) {
|
||||
return setMsg('unableToGetKey', 'warning');
|
||||
return setMsg(data.error, 'warning');
|
||||
} else {
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
|
|
|
@ -1 +1 @@
|
|||
(function(){var a,c,d,b,e;d=function(f,g){$("#msg").html(window.translate(f));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+g);if(g==="positive"){g="success"}return $("#color").addClass("alert-"+g)};a=function(g,f,i){var h;console.log("Error",i);h=JSON.parse(g.responseText);if(h&&h.error){h=h.error.replace(/.* /,"");console.log("Returned error",h);return d(h,"warning")}};c=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(f){var g;g=[{challenge:f.challenge,version:f.version}];d("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(f.appId,g,[],function(h){$("#u2fPermission").hide();if(h.errorCode){return d("unableToGetKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(h),challenge:JSON.stringify(f),keyName:$("#keyName").val()},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsRegistered","positive")}}},error:a})}})}})};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/unregistration",data:{},dataType:"json",error:a,success:function(f){if(f.error){return d("u2fFailed","warning")}else{if(f.result){return d("yourKeyIsUnregistered","positive")}}},error:a})};e=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(f){var g;g=[{keyHandle:f.keyHandle,version:f.version}];d("touchU2fDevice","positive");return u2f.sign(f.appId,f.challenge,g,function(h){if(h.errorCode){return d("unableToGetU2FKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(h),challenge:f.challenge},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsVerified","positive")}}},error:function(k,i,l){return console.log("error",l)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",c);$("#unregister").on("click",b);$("#verify").on("click",e);return $("#goback").attr("href",portal)})}).call(this);
|
||||
(function(){var a,c,d,b,e;d=function(f,g){$("#msg").html(window.translate(f));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+g);if(g==="positive"){g="success"}return $("#color").addClass("alert-"+g)};a=function(g,f,i){var h;console.log("Error",i);h=JSON.parse(g.responseText);if(h&&h.error){h=h.error.replace(/.* /,"");console.log("Returned error",h);return d(h,"warning")}};c=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(f){var g;g=[{challenge:f.challenge,version:f.version}];d("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(f.appId,g,[],function(h){$("#u2fPermission").hide();if(h.errorCode){return d(h.error,"warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(h),challenge:JSON.stringify(f),keyName:$("#keyName").val()},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsRegistered","positive")}}},error:a})}})}})};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/unregistration",data:{},dataType:"json",error:a,success:function(f){if(f.error){return d("u2fFailed","warning")}else{if(f.result){return d("yourKeyIsUnregistered","positive")}}},error:a})};e=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(f){var g;g=[{keyHandle:f.keyHandle,version:f.version}];d("touchU2fDevice","positive");return u2f.sign(f.appId,f.challenge,g,function(h){if(h.errorCode){return d("unableToGetU2FKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(h),challenge:f.challenge},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsVerified","positive")}}},error:function(k,i,l){return console.log("error",l)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",c);$("#unregister").on("click",b);$("#verify").on("click",e);return $("#goback").attr("href",portal)})}).call(this);
|
|
@ -147,10 +147,11 @@
|
|||
"logout":"تسجيل الخروج",
|
||||
"logoutConfirm":"هل تريد تسجيل الخروج؟",
|
||||
"logoutFromOtherApp":"تسجيل الخروج من التطبيقات الأخرى ...",
|
||||
"logoutFromSP":"Logout from service providers...",
|
||||
"logoutFromSP":"Logout from service providers ...",
|
||||
"mail":"البريد",
|
||||
"mailSent2":"تم إرسال رسالة إلى عنوان بريدك الإلكتروني.",
|
||||
"maintenanceMode":"هذا التطبيق في صيانة، يرجى محاولة الاتصال في وقت لاحق",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"رسالة جديدة (رسائل)",
|
||||
"newPassword":"كلمة مرور جديدة",
|
||||
|
|
|
@ -146,11 +146,12 @@
|
|||
"login":"Login",
|
||||
"logout":"Logout",
|
||||
"logoutConfirm":"Do you want to logout?",
|
||||
"logoutFromOtherApp":"Logout from other applications...",
|
||||
"logoutFromSP":"Logout from service providers...",
|
||||
"logoutFromOtherApp":"Logout from other applications ...",
|
||||
"logoutFromSP":"Logout from service providers ...",
|
||||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -146,11 +146,12 @@
|
|||
"login":"Login",
|
||||
"logout":"Logout",
|
||||
"logoutConfirm":"Do you want to logout?",
|
||||
"logoutFromOtherApp":"Logout from other applications...",
|
||||
"logoutFromSP":"Logout from service providers...",
|
||||
"logoutFromOtherApp":"Logout from other applications ...",
|
||||
"logoutFromSP":"Logout from service providers ...",
|
||||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -146,11 +146,12 @@
|
|||
"login":"Login",
|
||||
"logout":"Logout",
|
||||
"logoutConfirm":"Do you want to logout?",
|
||||
"logoutFromOtherApp":"Logout from other applications...",
|
||||
"logoutFromSP":"Logout from service providers...",
|
||||
"logoutFromOtherApp":"Logout from other applications ...",
|
||||
"logoutFromSP":"Logout from service providers ...",
|
||||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -147,11 +147,12 @@
|
|||
"logout":"Déconnexion",
|
||||
"logoutConfirm":"Souhaitez-vous vous déconnecter ?",
|
||||
"logoutFromOtherApp":"Déconnexion des autres applications...",
|
||||
"logoutFromSP":"Déconnexion des services...",
|
||||
"logoutFromSP":"Déconnexion des services ...",
|
||||
"mail":"Adresse mail",
|
||||
"mailSent2":"Un message a été envoyé à votre adresse mail.",
|
||||
"maintenanceMode":"Cette application est en maintenance, merci de réessayer plus tard",
|
||||
"name":"Nom",
|
||||
"maxNumberof2FDevicesReached":"Nombre maximum de second facteurs atteint !!!",
|
||||
"newMessages":"Nouveaux messages",
|
||||
"newPassword":"Nouveau mot de passe",
|
||||
"newPwdSentTo":"Une confirmation a été envoyée à votre adresse mail.",
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mail":"Mail",
|
||||
"mailSent2":"Vi é stato inviato un messaggio via mail",
|
||||
"maintenanceMode":"Questa applicazione è in manutenzione, prova a connetterti più tardi",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"Nuovo(i) messaggio(i)",
|
||||
"newPassword":"Nuova password",
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mail":"Mail",
|
||||
"mailSent2":"A message has been sent to your mail address.",
|
||||
"maintenanceMode":"This application is in maintenance, please try to connect later",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"New message(s)",
|
||||
"newPassword":"New password",
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mail":"Thư",
|
||||
"mailSent2":"Một tin nhắn đã được gửi đến địa chỉ thư của bạn.",
|
||||
"maintenanceMode":"Ứng dụng này đang trong quá trình bảo trì, hãy thử kết nối sau",
|
||||
"maxNumberof2FDevicesReached":"Maximum number of 2F devices reached !!!",
|
||||
"name":"Name",
|
||||
"newMessages":"(Các) tin nhắn mới",
|
||||
"newPassword":"Mật khẩu mới",
|
||||
|
|
Loading…
Reference in New Issue
Block a user