refact(oidc): manually open the browser (#15706)

* refact(oidc): manually open the browser

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): allow copying OIDC authentication links

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused translation in ko.rs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better hint on browser didn't open

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login handle exception

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): remove unused translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login handle error

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login in flight

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(translation): move "Continue" to the end of template.rs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): var rename

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): remove useless "open sign-in page"

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unecessary translation contents

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better way to show&expand the url

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better login ui

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): discard stale auth results after cancellation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): handle auth status query failures safely

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): prevent concurrent login operations

- reuse the active login dialog and block duplicate password submissions
- cancel only active OIDC operations when closing the dialog
- preserve authentication state until failure cancellation succeeds

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): refine login options error feedback

Preserve typed errors to hide the network tip for
HTTP failures and clarify the login-options API contract.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2026-08-04 12:35:04 +08:00
committed by GitHub
parent e6dd925ab0
commit a84bad4639
54 changed files with 703 additions and 186 deletions
+361 -86
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/user_model.dart';
@@ -11,6 +12,7 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
import './dialog.dart';
import './oidc_auth_status.dart';
const kOpSvgList = [
'github',
@@ -23,6 +25,8 @@ const kOpSvgList = [
'auth0',
'microsoft'
];
const _requestingAccountAuth = 'Requesting account auth';
const _waitingAccountAuth = 'Waiting account auth';
class _OidcProviderBranding {
final String label;
@@ -90,6 +94,7 @@ class ButtonOP extends StatelessWidget {
final Color primaryColor;
final double height;
final Function() onTap;
final bool Function() canStartAuth;
const ButtonOP({
Key? key,
@@ -99,6 +104,7 @@ class ButtonOP extends StatelessWidget {
required this.primaryColor,
required this.height,
required this.onTap,
required this.canStartAuth,
}) : super(key: key);
@override
@@ -111,11 +117,10 @@ class ButtonOP extends StatelessWidget {
width: 200,
child: Obx(() => ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: curOP.value.isEmpty || curOP.value == op
? primaryColor
: Colors.grey,
backgroundColor: primaryColor,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
onPressed: curOP.value.isEmpty || curOP.value == op ? onTap : null,
onPressed:
curOP.value == 'rustdesk' || !canStartAuth() ? null : onTap,
child: Row(
children: [
SizedBox(
@@ -145,15 +150,120 @@ class ConfigOP {
ConfigOP({required this.op, required this.icon});
}
class _OidcAuthController {
final RxString curOP = ''.obs;
Future<void> _pendingOperation = Future<void>.value();
int _authAttempt = 0;
bool _closed = false;
final _cancelInProgress = false.obs;
bool _isCurrent(int authAttempt, String op) {
return !_closed && authAttempt == _authAttempt && curOP.value == op;
}
Future<bool> start(String op) {
if (!canStart()) {
return Future<bool>.value(false);
}
final authAttempt = ++_authAttempt;
curOP.value = op;
// Web auth must start during the original user gesture so popups are allowed.
if (isWeb) {
return _startWeb(authAttempt, op);
}
final completer = Completer<bool>();
_pendingOperation = _pendingOperation.then((_) async {
if (!_isCurrent(authAttempt, op)) {
completer.complete(false);
return;
}
try {
await bind.mainAccountAuthCancel();
if (!_isCurrent(authAttempt, op)) {
completer.complete(false);
return;
}
await bind.mainAccountAuth(op: op, rememberMe: true);
completer.complete(_isCurrent(authAttempt, op));
} catch (error, stackTrace) {
completer.completeError(error, stackTrace);
}
});
return completer.future;
}
Future<bool> _startWeb(int authAttempt, String op) async {
await bind.mainAccountAuth(op: op, rememberMe: true);
return _isCurrent(authAttempt, op);
}
bool canStart() {
return !_closed && !_cancelInProgress.value;
}
Future<bool> cancelCurrent(String op) {
if (!canStart() || curOP.value != op) {
return Future<bool>.value(false);
}
final authAttempt = ++_authAttempt;
final completer = Completer<bool>();
_cancelInProgress.value = true;
_pendingOperation = _pendingOperation.then((_) async {
try {
await bind.mainAccountAuthCancel();
completer.complete(_isCurrent(authAttempt, op));
} catch (error, stackTrace) {
completer.completeError(error, stackTrace);
} finally {
_cancelInProgress.value = false;
}
});
return completer.future;
}
Future<void> _cancelBackend() async {
try {
await bind.mainAccountAuthCancel();
} catch (error, stackTrace) {
debugPrint('Failed to cancel account authentication $error');
debugPrintStack(stackTrace: stackTrace);
}
}
Future<void> close() async {
if (_closed) {
return;
}
final hasActiveOidcAuth =
curOP.value.isNotEmpty && curOP.value != 'rustdesk';
_closed = true;
_authAttempt++;
curOP.value = '';
if (hasActiveOidcAuth) {
await _cancelBackend();
}
await _pendingOperation;
if (hasActiveOidcAuth) {
await _cancelBackend();
}
}
}
class WidgetOP extends StatefulWidget {
final ConfigOP config;
final RxString curOP;
final Function(Map<String, dynamic>) cbLogin;
final Future<bool> Function(String) startAuth;
final Future<bool> Function(String) cancelAuth;
final bool Function() canStartAuth;
const WidgetOP({
Key? key,
required this.config,
required this.curOP,
required this.cbLogin,
required this.startAuth,
required this.cancelAuth,
required this.canStartAuth,
}) : super(key: key);
@override
@@ -164,6 +274,8 @@ class WidgetOP extends StatefulWidget {
class _WidgetOPState extends State<WidgetOP> {
Timer? _updateTimer;
bool _isAuthStatusQueryInFlight = false;
int _authAttempt = 0;
String _stateMsg = '';
String _failedMsg = '';
String _url = '';
@@ -174,55 +286,180 @@ class _WidgetOPState extends State<WidgetOP> {
_updateTimer?.cancel();
}
_beginQueryState() {
_beginQueryState(int authAttempt) {
_updateTimer?.cancel();
unawaited(_runAuthStatusQuery(() => _updateState(authAttempt)));
_updateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
_updateState();
unawaited(_runAuthStatusQuery(() => _updateState(authAttempt)));
});
}
_updateState() {
bind.mainAccountAuthResult().then((result) {
if (result.isEmpty) {
Future<void> _runAuthStatusQuery(Future<void> Function() query) async {
if (_isAuthStatusQueryInFlight) {
return;
}
_isAuthStatusQueryInFlight = true;
try {
await query();
} finally {
_isAuthStatusQueryInFlight = false;
}
}
Future<void> _launchAuthUrl(String url) async {
try {
final launched = await launchUrl(
Uri.parse(url),
mode: LaunchMode.externalApplication,
);
if (!launched) {
debugPrint('Failed to open OIDC authentication URL');
}
} catch (error, stackTrace) {
debugPrint(
'Failed to open OIDC authentication URL (${error.runtimeType})');
debugPrintStack(stackTrace: stackTrace);
}
}
Future<void> _copyAuthUrl(String url) async {
try {
await Clipboard.setData(ClipboardData(text: url));
showToast(
translate('Copied'),
);
} catch (error, stackTrace) {
debugPrint(
'Failed to copy OIDC authentication URL (${error.runtimeType})');
debugPrintStack(stackTrace: stackTrace);
showToast(translate('Failed'));
}
}
void _runCurrentAuthUrlAction(
int authAttempt,
String authUrl,
Future<void> Function(String) action,
) {
if (!mounted ||
authAttempt != _authAttempt ||
widget.curOP.value != widget.config.op ||
authUrl.isEmpty ||
_url != authUrl) {
return;
}
unawaited(action(authUrl));
}
void _invalidateAuthAttempt() {
_authAttempt++;
_url = '';
}
bool _isCurrentAuthAttempt(int authAttempt) {
return mounted &&
authAttempt == _authAttempt &&
widget.curOP.value == widget.config.op;
}
Future<void> _handleAuthFailure(
int authAttempt,
Object error,
String operation,
) async {
debugPrint('Failed to $operation $error');
if (!_isCurrentAuthAttempt(authAttempt)) {
return;
}
_updateTimer?.cancel();
setState(() => _failedMsg = 'Failed');
try {
final canceled = await widget.cancelAuth(widget.config.op);
if (!canceled || !_isCurrentAuthAttempt(authAttempt)) {
return;
}
} catch (cancelError, stackTrace) {
debugPrint('Failed to cancel account authentication $cancelError');
debugPrintStack(stackTrace: stackTrace);
return;
}
setState(() {
_invalidateAuthAttempt();
widget.curOP.value = '';
});
}
Future<void> _updateState(int authAttempt) {
if (!mounted ||
authAttempt != _authAttempt ||
widget.curOP.value != widget.config.op) {
_updateTimer?.cancel();
return Future<void>.value();
}
return bind.mainAccountAuthResult().then<void>((result) {
if (!mounted ||
authAttempt != _authAttempt ||
widget.curOP.value != widget.config.op ||
result.isEmpty) {
return;
}
final resultMap = jsonDecode(result);
if (resultMap == null) {
return;
}
final String stateMsg = resultMap['state_msg'];
final String backendStateMsg = resultMap['state_msg'];
String failedMsg = resultMap['failed_msg'];
final String? url = resultMap['url'];
final stateMsg = backendStateMsg == _requestingAccountAuth &&
(url == null || url.isEmpty)
? _waitingAccountAuth
: backendStateMsg;
final bool urlLaunched = (resultMap['url_launched'] as bool?) ?? false;
final authBody = resultMap['auth_body'];
if (_stateMsg != stateMsg || _failedMsg != failedMsg) {
if (_url.isEmpty && url != null && url.isNotEmpty) {
if (!urlLaunched) {
launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
}
_url = url;
}
if (authBody != null) {
_updateTimer?.cancel();
widget.curOP.value = '';
widget.cbLogin(authBody as Map<String, dynamic>);
}
setState(() {
_stateMsg = stateMsg;
_failedMsg = failedMsg;
if (failedMsg.isNotEmpty) {
widget.curOP.value = '';
_updateTimer?.cancel();
}
});
if (authBody != null) {
_updateTimer?.cancel();
_invalidateAuthAttempt();
widget.curOP.value = '';
widget.cbLogin(authBody as Map<String, dynamic>);
return;
}
});
final stateChanged = _stateMsg != stateMsg || _failedMsg != failedMsg;
final newUrl = _url.isEmpty && url != null && url.isNotEmpty ? url : null;
if (!stateChanged && newUrl == null) {
return;
}
setState(() {
_stateMsg = stateMsg;
_failedMsg = failedMsg;
if (newUrl != null) {
_url = newUrl;
}
if (failedMsg.isNotEmpty) {
_invalidateAuthAttempt();
widget.curOP.value = '';
_updateTimer?.cancel();
}
});
if (newUrl != null && failedMsg.isEmpty && !urlLaunched) {
unawaited(_launchAuthUrl(newUrl));
}
}).catchError(
(e) => _handleAuthFailure(
authAttempt,
e,
'query account authentication',
),
);
}
_resetState() {
_stateMsg = '';
_failedMsg = '';
_url = '';
int _resetState() {
_updateTimer?.cancel();
setState(() {
_invalidateAuthAttempt();
_stateMsg = _waitingAccountAuth;
_failedMsg = '';
});
return _authAttempt;
}
@override
@@ -235,11 +472,31 @@ class _WidgetOPState extends State<WidgetOP> {
icon: widget.config.icon,
primaryColor: str2color(widget.config.op, 0x7f),
height: 36,
canStartAuth: widget.canStartAuth,
onTap: () async {
_resetState();
widget.curOP.value = widget.config.op;
await bind.mainAccountAuth(op: widget.config.op, rememberMe: true);
_beginQueryState();
if (!widget.canStartAuth()) {
return;
}
final authAttempt = _resetState();
try {
final started = await widget.startAuth(widget.config.op);
if (!started) {
return;
}
} catch (e) {
await _handleAuthFailure(
authAttempt,
e,
'start account authentication',
);
return;
}
if (!mounted ||
authAttempt != _authAttempt ||
widget.curOP.value != widget.config.op) {
return;
}
_beginQueryState(authAttempt);
},
),
Obx(() {
@@ -247,6 +504,8 @@ class _WidgetOPState extends State<WidgetOP> {
widget.curOP.value != widget.config.op) {
_failedMsg = '';
}
final authAttempt = _authAttempt;
final authUrl = _url;
return Offstage(
offstage:
_failedMsg.isEmpty && widget.curOP.value != widget.config.op,
@@ -256,11 +515,20 @@ class _WidgetOPState extends State<WidgetOP> {
if (_stateMsg.isNotEmpty && _failedMsg.isEmpty)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: SelectableText(
translate(_stateMsg),
style: DefaultTextStyle.of(context)
.style
.copyWith(fontSize: 12),
child: OidcAuthStatus(
message: translate(_stateMsg),
browserFallbackPrompt: translate(
"Browser didn't open? Use the url below to sign in.",
),
authUrl: authUrl,
copyLabel: translate('Copy to clipboard'),
onCopy: authUrl.isEmpty
? null
: () => _runCurrentAuthUrlAction(
authAttempt,
authUrl,
_copyAuthUrl,
),
),
),
if (_failedMsg.isNotEmpty)
@@ -304,34 +572,6 @@ class _WidgetOPState extends State<WidgetOP> {
),
);
}),
Obx(
() => Offstage(
offstage: widget.curOP.value != widget.config.op,
child: const SizedBox(
height: 5.0,
),
),
),
Obx(
() => Offstage(
offstage: widget.curOP.value != widget.config.op,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 20),
child: ElevatedButton(
onPressed: () {
widget.curOP.value = '';
_updateTimer?.cancel();
_resetState();
bind.mainAccountAuthCancel();
},
child: Text(
translate('Cancel'),
style: TextStyle(fontSize: 15),
),
),
),
),
),
],
);
}
@@ -341,12 +581,18 @@ class LoginWidgetOP extends StatelessWidget {
final List<ConfigOP> ops;
final RxString curOP;
final Function(Map<String, dynamic>) cbLogin;
final Future<bool> Function(String) startAuth;
final Future<bool> Function(String) cancelAuth;
final bool Function() canStartAuth;
LoginWidgetOP({
Key? key,
required this.ops,
required this.curOP,
required this.cbLogin,
required this.startAuth,
required this.cancelAuth,
required this.canStartAuth,
}) : super(key: key);
@override
@@ -357,6 +603,9 @@ class LoginWidgetOP extends StatelessWidget {
config: op,
curOP: curOP,
cbLogin: cbLogin,
startAuth: startAuth,
cancelAuth: cancelAuth,
canStartAuth: canStartAuth,
),
const Divider(
indent: 5,
@@ -434,12 +683,11 @@ class LoginWidgetUserPass extends StatelessWidget {
translate('Login'),
style: TextStyle(fontSize: 16),
),
onPressed:
curOP.value.isEmpty || curOP.value == 'rustdesk'
? () {
onLogin();
}
: null,
onPressed: curOP.value.isEmpty && !isInProgress
? () {
onLogin();
}
: null,
)),
),
])),
@@ -450,8 +698,28 @@ class LoginWidgetUserPass extends StatelessWidget {
const kAuthReqTypeOidc = 'oidc/';
Future<bool?>? _activeLoginDialog;
// call this directly
Future<bool?> loginDialog() async {
Future<bool?> loginDialog() {
final activeDialog = _activeLoginDialog;
if (activeDialog != null) {
return activeDialog;
}
final dialog = _openLoginDialogOnce();
_activeLoginDialog = dialog;
return dialog;
}
Future<bool?> _openLoginDialogOnce() async {
try {
return await _openLoginDialog();
} finally {
_activeLoginDialog = null;
}
}
Future<bool?> _openLoginDialog() async {
var username =
TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? '');
var password = TextEditingController();
@@ -461,12 +729,13 @@ Future<bool?> loginDialog() async {
String? usernameMsg;
String? passwordMsg;
var isInProgress = false;
final RxString curOP = ''.obs;
final oidcAuth = _OidcAuthController();
final curOP = oidcAuth.curOP;
// Track hover state for the close icon
bool isCloseHovered = false;
final loginOptions = [].obs;
final loginOptionsError = Rxn<String>();
final loginOptionsError = Rxn<Object>();
final loginOptionsInProgress = false.obs;
fetchLoginOptions() async {
loginOptionsInProgress.value = true;
@@ -475,7 +744,7 @@ Future<bool?> loginDialog() async {
loginOptionsError.value = null;
} catch (e) {
debugPrint("queryOidcLoginOptions failed: $e");
loginOptionsError.value = e.toString();
loginOptionsError.value = e;
} finally {
loginOptionsInProgress.value = false;
}
@@ -555,6 +824,9 @@ Future<bool?> loginDialog() async {
}
onLogin() async {
if (curOP.value.isNotEmpty || isInProgress) {
return;
}
// validate
if (username.text.isEmpty) {
setState(() => usernameMsg = translate('Username missed'));
@@ -593,7 +865,7 @@ Future<bool?> loginDialog() async {
const SizedBox(height: 8.0),
// NOT use Offstage to wrap LinearProgressIndicator
if (inProgress) const LinearProgressIndicator(),
if (!inProgress)
if (!inProgress && error is! RequestException)
Text(
translate('network_error_tip'),
style: const TextStyle(fontSize: 12),
@@ -608,7 +880,7 @@ Future<bool?> loginDialog() async {
),
if (!inProgress)
SelectableText(
error,
error.toString(),
style: const TextStyle(fontSize: 11, color: Colors.red),
textAlign: TextAlign.center,
),
@@ -635,6 +907,9 @@ Future<bool?> loginDialog() async {
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
.toList(),
curOP: curOP,
startAuth: oidcAuth.start,
cancelAuth: oidcAuth.cancelCurrent,
canStartAuth: oidcAuth.canStart,
cbLogin: (Map<String, dynamic> authBody) async {
LoginResponse? resp;
try {
@@ -716,7 +991,7 @@ Future<bool?> loginDialog() async {
onCancel: onDialogCancel,
onSubmit: onLogin,
);
});
}).whenComplete(oidcAuth.close);
if (res != null) {
await UserModel.updateOtherModels();
@@ -0,0 +1,157 @@
import 'package:flutter/material.dart';
const _statusFontSize = 12.0;
const _statusSpacing = 4.0;
const _messageActionSpacing = 8.0;
const _desktopActionSize = 28.0;
const _touchPlatforms = <TargetPlatform>{
TargetPlatform.android,
TargetPlatform.iOS,
TargetPlatform.fuchsia,
};
class OidcAuthStatus extends StatelessWidget {
final String message;
final String browserFallbackPrompt;
final String authUrl;
final String copyLabel;
final VoidCallback? onCopy;
const OidcAuthStatus({
super.key,
required this.message,
required this.browserFallbackPrompt,
required this.authUrl,
required this.copyLabel,
this.onCopy,
});
@override
Widget build(BuildContext context) {
final messageStyle =
DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SelectableText(message, style: messageStyle),
if (authUrl.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: _messageActionSpacing),
child: _OidcAuthFallback(
browserFallbackPrompt: browserFallbackPrompt,
authUrl: authUrl,
copyLabel: copyLabel,
onCopy: onCopy,
),
),
],
);
}
}
class _OidcAuthFallback extends StatefulWidget {
final String browserFallbackPrompt;
final String authUrl;
final String copyLabel;
final VoidCallback? onCopy;
const _OidcAuthFallback({
required this.browserFallbackPrompt,
required this.authUrl,
required this.copyLabel,
required this.onCopy,
});
@override
State<_OidcAuthFallback> createState() => _OidcAuthFallbackState();
}
class _OidcAuthFallbackState extends State<_OidcAuthFallback> {
bool _expanded = false;
@override
void didUpdateWidget(covariant _OidcAuthFallback oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.authUrl != widget.authUrl) {
_expanded = false;
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final helperStyle = DefaultTextStyle.of(context).style.copyWith(
fontSize: _statusFontSize,
color: theme.colorScheme.onSurfaceVariant,
);
final linkColor = theme.brightness == Brightness.dark
? Colors.blue.shade300
: Colors.blue.shade800;
final isTouchPlatform = _touchPlatforms.contains(theme.platform);
final actionSize =
isTouchPlatform ? kMinInteractiveDimension : _desktopActionSize;
final urlStyle =
DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.browserFallbackPrompt,
style: helperStyle,
textAlign: TextAlign.center,
),
Padding(
padding: const EdgeInsets.only(top: _statusSpacing),
child: _buildUrl(urlStyle, linkColor, actionSize),
),
],
);
}
void _copyAndExpand() {
setState(() => _expanded = true);
widget.onCopy?.call();
}
Widget _buildUrl(TextStyle urlStyle, Color linkColor, double actionSize) {
final collapsedUrl = SizedBox(
width: double.infinity,
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: linkColor,
minimumSize: Size(0, actionSize),
padding: EdgeInsets.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.standard,
),
onPressed: _copyAndExpand,
child: Text(
widget.authUrl,
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: urlStyle.copyWith(
color: linkColor,
decoration: TextDecoration.underline,
),
),
),
);
final collapsedChild = widget.onCopy == null
? collapsedUrl
: Tooltip(message: widget.copyLabel, child: collapsedUrl);
return Container(
width: double.infinity,
constraints: BoxConstraints(minHeight: actionSize),
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: _messageActionSpacing),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(_statusSpacing),
),
child: _expanded
? SelectableText(widget.authUrl, style: urlStyle)
: collapsedChild,
);
}
}
+3 -2
View File
@@ -234,8 +234,9 @@ class UserModel {
return loginResponse;
}
/// Throws on network failure so callers can surface the error and offer a
/// retry; returns an empty list when the server has no third-party login.
/// Throws on network failures, non-success responses, and invalid response
/// data. Returns an empty list when no API server is configured or a
/// successful response contains no third-party login options.
static Future<List<dynamic>> queryOidcLoginOptions() async {
final url = await bind.mainGetApiServer();
if (url.trim().isEmpty) return [];
+82 -49
View File
@@ -113,7 +113,7 @@ pub struct OidcSession {
failed_msg: String,
code_url: Option<OidcAuthUrl>,
auth_body: Option<AuthBody>,
keep_querying: bool,
auth_attempt: u64,
running: bool,
query_timeout: Duration,
}
@@ -140,7 +140,7 @@ impl OidcSession {
failed_msg: "".to_owned(),
code_url: None,
auth_body: None,
keep_querying: false,
auth_attempt: 0,
running: false,
query_timeout: Duration::from_secs(QUERY_TIMEOUT_SECS),
}
@@ -192,12 +192,8 @@ impl OidcSession {
body: String,
}
let resp = crate::http_request_sync(
url.to_string(),
"GET".to_owned(),
None,
"{}".to_owned(),
)?;
let resp =
crate::http_request_sync(url.to_string(), "GET".to_owned(), None, "{}".to_owned())?;
let resp = serde_json::from_str::<HttpResponseBody>(&resp)?;
HbbHttpResponse::parse(&resp.body)
}
@@ -205,7 +201,6 @@ impl OidcSession {
fn reset(&mut self) {
self.state_msg = REQUESTING_ACCOUNT_AUTH;
self.failed_msg = "".to_owned();
self.keep_querying = true;
self.running = false;
self.code_url = None;
self.auth_body = None;
@@ -220,49 +215,92 @@ impl OidcSession {
self.running = false;
}
fn start_auth_attempt(&mut self) -> u64 {
self.auth_attempt = self.auth_attempt.wrapping_add(1);
self.auth_attempt
}
fn cancel_auth_attempt(&mut self) {
self.auth_attempt = self.auth_attempt.wrapping_add(1);
}
fn is_current_auth_attempt(&self, auth_attempt: u64) -> bool {
self.auth_attempt == auth_attempt
}
fn auth_attempt_is_current(auth_attempt: u64) -> bool {
OIDC_SESSION
.read()
.unwrap()
.is_current_auth_attempt(auth_attempt)
}
fn set_state_if_current(auth_attempt: u64, state_msg: &'static str, failed_msg: String) {
let mut session = OIDC_SESSION.write().unwrap();
if session.is_current_auth_attempt(auth_attempt) {
session.set_state(state_msg, failed_msg);
}
}
fn sleep(secs: f32) {
std::thread::sleep(std::time::Duration::from_secs_f32(secs));
}
fn auth_task(api_server: String, op: String, id: String, uuid: String, remember_me: bool) {
fn auth_task(
api_server: String,
op: String,
id: String,
uuid: String,
remember_me: bool,
auth_attempt: u64,
) {
let auth_request_res = Self::auth(&api_server, &op, &id, &uuid);
log::info!("Request oidc auth result: {:?}", &auth_request_res);
if !Self::auth_attempt_is_current(auth_attempt) {
return;
}
let code_url = match auth_request_res {
Ok(HbbHttpResponse::<_>::Data(code_url)) => code_url,
Ok(HbbHttpResponse::<_>::Error(err)) => {
OIDC_SESSION
.write()
.unwrap()
.set_state(REQUESTING_ACCOUNT_AUTH, err);
Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err);
return;
}
Ok(_) => {
OIDC_SESSION
.write()
.unwrap()
.set_state(REQUESTING_ACCOUNT_AUTH, "Invalid auth response".to_owned());
Self::set_state_if_current(
auth_attempt,
REQUESTING_ACCOUNT_AUTH,
"Invalid auth response".to_owned(),
);
return;
}
Err(err) => {
OIDC_SESSION
.write()
.unwrap()
.set_state(REQUESTING_ACCOUNT_AUTH, err.to_string());
Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err.to_string());
return;
}
};
OIDC_SESSION
.write()
.unwrap()
.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
OIDC_SESSION.write().unwrap().code_url = Some(code_url.clone());
{
let mut session = OIDC_SESSION.write().unwrap();
if !session.is_current_auth_attempt(auth_attempt) {
return;
}
session.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
session.code_url = Some(code_url.clone());
}
let begin = Instant::now();
let query_timeout = OIDC_SESSION.read().unwrap().query_timeout;
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
match Self::query(&api_server, &code_url.code, &id, &uuid) {
while Self::auth_attempt_is_current(auth_attempt) && begin.elapsed() < query_timeout {
let query_result = Self::query(&api_server, &code_url.code, &id, &uuid);
if !Self::auth_attempt_is_current(auth_attempt) {
return;
}
match query_result {
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
let mut session = OIDC_SESSION.write().unwrap();
if !session.is_current_auth_attempt(auth_attempt) {
return;
}
if auth_body.r#type == "access_token" {
if remember_me {
LocalConfig::set_option(
@@ -281,21 +319,15 @@ impl OidcSession {
);
}
}
OIDC_SESSION
.write()
.unwrap()
.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
OIDC_SESSION.write().unwrap().auth_body = Some(auth_body);
session.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
session.auth_body = Some(auth_body);
return;
}
Ok(HbbHttpResponse::<_>::Error(err)) => {
if err.contains("No authed oidc is found") {
// ignore, keep querying
} else {
OIDC_SESSION
.write()
.unwrap()
.set_state(WAITING_ACCOUNT_AUTH, err);
Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, err);
return;
}
}
@@ -310,14 +342,9 @@ impl OidcSession {
Self::sleep(QUERY_INTERVAL_SECS);
}
if begin.elapsed() >= query_timeout {
OIDC_SESSION
.write()
.unwrap()
.set_state(WAITING_ACCOUNT_AUTH, "timeout".to_owned());
if begin.elapsed() >= query_timeout && Self::auth_attempt_is_current(auth_attempt) {
Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, "timeout".to_owned());
}
// no need to handle "keep_querying == false"
}
fn set_state(&mut self, state_msg: &'static str, failed_msg: String) {
@@ -339,11 +366,17 @@ impl OidcSession {
uuid: String,
remember_me: bool,
) {
Self::auth_cancel();
let auth_attempt = OIDC_SESSION.write().unwrap().start_auth_attempt();
Self::wait_stop_querying();
OIDC_SESSION.write().unwrap().before_task();
{
let mut session = OIDC_SESSION.write().unwrap();
if !session.is_current_auth_attempt(auth_attempt) {
return;
}
session.before_task();
}
std::thread::spawn(move || {
Self::auth_task(api_server, op, id, uuid, remember_me);
Self::auth_task(api_server, op, id, uuid, remember_me, auth_attempt);
OIDC_SESSION.write().unwrap().after_task();
});
}
@@ -358,7 +391,7 @@ impl OidcSession {
}
pub fn auth_cancel() {
OIDC_SESSION.write().unwrap().keep_querying = false;
OIDC_SESSION.write().unwrap().cancel_auth_attempt();
}
pub fn get_result() -> AuthResult {
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "اتصال الوسيط"),
("Secure Connection", "اتصال آمن"),
("Insecure Connection", "اتصال غير آمن"),
("Continue", ""),
("Scale original", "المقياس الأصلي"),
("Scale adaptive", "مقياس التكيف"),
("General", "عام"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "تم حظر عنوان IP الخاص بك من قبل الطرف الآخر"),
("id_whitelist_caveat_tip", "يتم الإبلاغ عن المعرف من قبل العميل المتصل. القائمة البيضاء تقلل من التعرض ولا تغني عن كلمة المرور أو 2FA"),
("whitelist_cidr_tip", "يتم دعم صيغة CIDR، مثال: 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Рэтрансляванае падключэнне"),
("Secure Connection", "Бяспечнае падключэнне"),
("Insecure Connection", "Нябяспечнае падключэнне"),
("Continue", ""),
("Scale original", "Арыгінальны маштаб"),
("Scale adaptive", "Адаптыўны маштаб"),
("General", "Агульныя"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Ваш IP-адрас заблакаваны аддаленай прыладай"),
("id_whitelist_caveat_tip", "ID паведамляецца кліентам, які падключаецца. Белы спіс памяншае паверхню атакі і не замяняе пароль або 2FA"),
("whitelist_cidr_tip", "Падтрымліваецца натацыя CIDR, напрыклад: 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Релейна връзка"),
("Secure Connection", "Сигурна връзка"),
("Insecure Connection", "Несигурна връзка"),
("Continue", ""),
("Scale original", "Оригинален мащаб"),
("Scale adaptive", "Приспособимо мащабиране"),
("General", "Основен"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Вашият IP адрес е блокиран от отсрещната страна"),
("id_whitelist_caveat_tip", "ID се съобщава от свързващия се клиент. Белият списък намалява изложеността и не замества паролата или 2FA"),
("whitelist_cidr_tip", "Поддържа се CIDR нотация, например: 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Connexió amb repetidor"),
("Secure Connection", "Connexió segura"),
("Insecure Connection", "Connexió no segura"),
("Continue", ""),
("Scale original", "Escala original"),
("Scale adaptive", "Escala adaptativa"),
("General", "General"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "La vostra IP està bloquejada per l'altre extrem"),
("id_whitelist_caveat_tip", "L'ID és informat pel client que es connecta. Aquesta llista blanca redueix l'exposició i no substitueix la contrasenya ni la 2FA"),
("whitelist_cidr_tip", "S'admet la notació CIDR, per exemple 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "中继连接"),
("Secure Connection", "安全连接"),
("Insecure Connection", "非安全连接"),
("Continue", "继续"),
("Scale original", "原始尺寸"),
("Scale adaptive", "适应窗口"),
("General", "常规"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "你的 IP 已被对方阻止"),
("id_whitelist_caveat_tip", "ID 由对端客户端上报,白名单用于减少暴露面,不能替代密码或 2FA"),
("whitelist_cidr_tip", "支持 CIDR 写法,例如 192.168.1.0/24"),
("Continue", "继续"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Připojení předávací server"),
("Secure Connection", "Zabezpečené připojení"),
("Insecure Connection", "Nezabezpečené připojení"),
("Continue", ""),
("Scale original", "Originální měřítko"),
("Scale adaptive", "Adaptivní měřítko"),
("General", "Obecné"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vaše IP adresa je protistranou blokována"),
("id_whitelist_caveat_tip", "ID je hlášeno připojujícím se klientem. Tento seznam snižuje vystavení a nenahrazuje heslo ani 2FA"),
("whitelist_cidr_tip", "Je podporován zápis CIDR, například 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Viderestillingsforbindelse"),
("Secure Connection", "Sikker forbindelse"),
("Insecure Connection", "Usikker forbindelse"),
("Continue", ""),
("Scale original", "Original skalering"),
("Scale adaptive", "Adaptiv skalering"),
("General", "Generelt"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Din IP-adresse er blokeret af modparten"),
("id_whitelist_caveat_tip", "ID'et rapporteres af den klient, der opretter forbindelse. Whitelisten reducerer eksponeringen og erstatter ikke adgangskode eller 2FA"),
("whitelist_cidr_tip", "CIDR-notation understøttes, f.eks. 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Relay-Verbindung"),
("Secure Connection", "Sichere Verbindung"),
("Insecure Connection", "Unsichere Verbindung"),
("Continue", "Weiter"),
("Scale original", "Keine Skalierung"),
("Scale adaptive", "Anpassbare Skalierung"),
("General", "Allgemein"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Ihre IP-Adresse wird von der Gegenstelle blockiert"),
("id_whitelist_caveat_tip", "Die ID wird vom verbindenden Client gemeldet. Die Whitelist verringert die Angriffsfläche und ersetzt weder Passwort noch 2FA."),
("whitelist_cidr_tip", "Die CIDR-Notation wird unterstützt, z. B. 192.168.1.0/24"),
("Continue", "Weiter"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Αναμεταδιδόμενη σύνδεση"),
("Secure Connection", "Ασφαλής σύνδεση"),
("Insecure Connection", "Μη ασφαλής σύνδεση"),
("Continue", ""),
("Scale original", "Κλιμάκωση πρωτότυπου"),
("Scale adaptive", "Προσαρμοσμένη κλίμακα"),
("General", "Γενικά"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Η διεύθυνση IP σας έχει αποκλειστεί από τον απομακρυσμένο υπολογιστή"),
("id_whitelist_caveat_tip", "Το ID αναφέρεται από τον πελάτη που συνδέεται. Η λίστα επιτρεπόμενων μειώνει την έκθεση και δεν αντικαθιστά τον κωδικό πρόσβασης ή το 2FA"),
("whitelist_cidr_tip", "Υποστηρίζεται η σημειογραφία CIDR, π.χ. 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Relajsa Konekto"),
("Secure Connection", "Sekura Konekto"),
("Insecure Connection", "Nesekura Konekto"),
("Continue", ""),
("Scale original", "Skalo originalo"),
("Scale adaptive", "Skalo adapta"),
("General", "Ĝenerala"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Via IP estas blokita de la alia flanko"),
("id_whitelist_caveat_tip", "La ID estas raportata de la konektiĝanta kliento. La blanka listo malpliigas la eksponiĝon kaj ne anstataŭas la pasvorton aŭ 2FA"),
("whitelist_cidr_tip", "La notacio CIDR estas subtenata, ekzemple 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Conexión Relay"),
("Secure Connection", "Conexión segura"),
("Insecure Connection", "Conexión insegura"),
("Continue", ""),
("Scale original", "Escala original"),
("Scale adaptive", "Escala adaptativa"),
("General", "General"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Tu IP está bloqueada por el dispositivo remoto"),
("id_whitelist_caveat_tip", "El ID lo comunica el cliente que se conecta. Esta lista blanca reduce la exposición y no sustituye a la contraseña ni al 2FA"),
("whitelist_cidr_tip", "Se admite la notación CIDR, por ejemplo 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Releeühendus"),
("Secure Connection", "Turvaline ühendus"),
("Insecure Connection", "Ebaturvaline ühendus"),
("Continue", ""),
("Scale original", "Originaalskaala"),
("Scale adaptive", "Kohanduv skaala"),
("General", "Üldine"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Teine pool on sinu IP-aadressi blokeerinud"),
("id_whitelist_caveat_tip", "ID edastab ühenduv klient. Lubamisloend vähendab eksponeeritust ega asenda parooli või 2FA-d"),
("whitelist_cidr_tip", "Toetatud on CIDR-tähistus, näiteks 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Konexio igorria"),
("Secure Connection", "Konexio segurua"),
("Insecure Connection", "Konexio ez-segurua"),
("Continue", ""),
("Scale original", "Jatorrizko eskala"),
("Scale adaptive", "Eskala moldagarria"),
("General", "Orokorra"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Beste aldeak zure IP helbidea blokeatu du"),
("id_whitelist_caveat_tip", "IDa konektatzen den bezeroak jakinarazten du. Zerrenda honek esposizioa murrizten du eta ez du pasahitza edo 2FA ordezkatzen"),
("whitelist_cidr_tip", "CIDR notazioa onartzen da, adibidez 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Relay ارتباط"),
("Secure Connection", "ارتباط امن"),
("Insecure Connection", "ارتباط غیر امن"),
("Continue", ""),
("Scale original", "مقیاس اصلی"),
("Scale adaptive", "مقیاس تطبیقی"),
("General", "عمومی"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "نشانی IP شما توسط طرف مقابل مسدود شده است"),
("id_whitelist_caveat_tip", "شناسه توسط کلاینت متصل شونده گزارش می شود. لیست مجاز سطح در معرض بودن را کاهش می دهد و جایگزین رمز عبور یا 2FA نیست"),
("whitelist_cidr_tip", "نماد CIDR پشتیبانی می شود، برای مثال 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Välitetty yhteys"),
("Secure Connection", "Suojattu yhteys"),
("Insecure Connection", "Suojaamaton yhteys"),
("Continue", ""),
("Scale original", "Skaalaa alkuperäinen"),
("Scale adaptive", "Mukautuva skaalaus"),
("General", "Yleiset"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vastapuoli on estänyt IP-osoitteesi"),
("id_whitelist_caveat_tip", "ID on yhdistävän asiakkaan ilmoittama. Sallintalista pienentää altistusta eikä korvaa salasanaa tai 2FA:ta"),
("whitelist_cidr_tip", "CIDR-merkintä on tuettu, esimerkiksi 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Connexion via relais"),
("Secure Connection", "Connexion sécurisée"),
("Insecure Connection", "Connexion non sécurisée"),
("Continue", ""),
("Scale original", "Échelle originale"),
("Scale adaptive", "Échelle adaptative"),
("General", "Général"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Votre adresse IP est bloquée par lappareil distant"),
("id_whitelist_caveat_tip", "LID est déclaré par le client qui se connecte. Cette liste blanche réduit lexposition et ne remplace ni le mot de passe ni la 2FA"),
("whitelist_cidr_tip", "La notation CIDR est prise en charge, par exemple 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "რეტრანსლირებული კავშირი"),
("Secure Connection", "უსაფრთხო კავშირი"),
("Insecure Connection", "არაუსაფრთხო კავშირი"),
("Continue", ""),
("Scale original", "ორიგინალური მასშტაბი"),
("Scale adaptive", "ადაპტირებადი მასშტაბი"),
("General", "ზოგადი"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "თქვენი IP მისამართი დაბლოკილია მეორე მხარის მიერ"),
("id_whitelist_caveat_tip", "ID-ს აცხადებს დამაკავშირებელი კლიენტი. თეთრი სია ამცირებს ექსპოზიციას და ვერ ჩაანაცვლებს პაროლს ან 2FA-ს"),
("whitelist_cidr_tip", "მხარდაჭერილია CIDR ჩანაწერი, მაგალითად 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "રિલે કનેક્શન"),
("Secure Connection", "સુરક્ષિત કનેક્શન"),
("Insecure Connection", "અસુરક્ષિત કનેક્શન"),
("Continue", ""),
("Scale original", "મૂળ સ્કેલ"),
("Scale adaptive", "એડેપ્ટિવ સ્કેલ"),
("General", "સામાન્ય"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "તમારું IP સામેના પક્ષ દ્વારા બ્લોક કરવામાં આવ્યું છે"),
("id_whitelist_caveat_tip", "ID કનેક્ટ થતા ક્લાયન્ટ દ્વારા જણાવવામાં આવે છે. વ્હાઇટલિસ્ટ એક્સપોઝર ઘટાડે છે અને પાસવર્ડ કે 2FA નો વિકલ્પ નથી"),
("whitelist_cidr_tip", "CIDR નોટેશન સપોર્ટેડ છે, ઉदાહરણ તરીકે 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "חיבור באמצעות ממסר"),
("Secure Connection", "חיבור מאובטח"),
("Insecure Connection", "חיבור לא מאובטח"),
("Continue", ""),
("Scale original", "קנה מידה מקורי"),
("Scale adaptive", "קנה מידה מותאם"),
("General", "כללי"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "כתובת ה-IP שלך נחסמה על ידי הצד המרוחק"),
("id_whitelist_caveat_tip", "המזהה מדווח על ידי הלקוח המתחבר. הרשימה הלבנה מצמצמת חשיפה ואינה מחליפה סיסמה או 2FA"),
("whitelist_cidr_tip", "יש תמיכה בסימון CIDR, לדוגמה 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "रिले कनेक्शन"),
("Secure Connection", "सुरक्षित कनेक्शन"),
("Insecure Connection", "असुरक्षित कनेक्शन"),
("Continue", ""),
("Scale original", "मूल पैमाना"),
("Scale adaptive", "अनुकूली पैमाना"),
("General", "सामान्य"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "आपका IP दूसरे पक्ष द्वारा अवरुद्ध कर दिया गया है"),
("id_whitelist_caveat_tip", "ID कनेक्ट करने वाले क्लाइंट द्वारा बताई जाती है। श्वेतसूची जोखिम कम करती है और पासवर्ड या 2FA का विकल्प नहीं है"),
("whitelist_cidr_tip", "CIDR नोटेशन समर्थित है, उदाहरण के लिए 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Posredna veza"),
("Secure Connection", "Sigurna veza"),
("Insecure Connection", "Nesigurna veza"),
("Continue", ""),
("Scale original", "Skaliraj izvornik"),
("Scale adaptive", "Prilagođeno skaliranje"),
("General", "Općenito"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vašu IP adresu je blokiralo udaljeno računalo"),
("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamjenjuje lozinku ni 2FA"),
("whitelist_cidr_tip", "Podržan je CIDR zapis, primjerice 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Kapcsolódás továbbító-kiszolgálón keresztül"),
("Secure Connection", "Biztonságos kapcsolat"),
("Insecure Connection", "Nem biztonságos kapcsolat"),
("Continue", ""),
("Scale original", "Eredeti méretarány"),
("Scale adaptive", "Adaptív méretarány"),
("General", "Általános"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Az IP-címét a távoli fél letiltotta"),
("id_whitelist_caveat_tip", "Az azonosítót a csatlakozó kliens jelenti. Az engedélyezési lista csökkenti a kitettséget, és nem helyettesíti a jelszót vagy a 2FA-t"),
("whitelist_cidr_tip", "A CIDR jelölés támogatott, például 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Koneksi Relay"),
("Secure Connection", "Koneksi aman"),
("Insecure Connection", "Koneksi Tidak Aman"),
("Continue", ""),
("Scale original", "Skala asli"),
("Scale adaptive", "Skala adaptif"),
("General", "Umum"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP Anda diblokir oleh perangkat remote"),
("id_whitelist_caveat_tip", "ID dilaporkan oleh klien yang terhubung. Daftar ini mengurangi paparan dan bukan pengganti kata sandi atau 2FA"),
("whitelist_cidr_tip", "Notasi CIDR didukung, misalnya 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Connessione relay"),
("Secure Connection", "Connessione sicura"),
("Insecure Connection", "Connessione non sicura"),
("Continue", "Continua"),
("Scale original", "Scala originale"),
("Scale adaptive", "Scala adattiva"),
("General", "Generale"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Il tuo IP è bloccato dal dispositivo remoto"),
("id_whitelist_caveat_tip", "L'ID è dichiarato dal client che si connette. Questo elenco riduce l'esposizione e non sostituisce la password o la 2FA"),
("whitelist_cidr_tip", "È supportata la notazione CIDR, ad esempio 192.168.1.0/24"),
("Continue", "Continua"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "中継接続"),
("Secure Connection", "安全な接続"),
("Insecure Connection", "安全でない接続"),
("Continue", ""),
("Scale original", "オリジナルのサイズ"),
("Scale adaptive", "ウィンドウに合わせる"),
("General", "一般"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "あなたの IP アドレスは接続先によってブロックされています"),
("id_whitelist_caveat_tip", "ID は接続するクライアントから申告されます。ホワイトリストは露出を減らすもので、パスワードや 2FA の代わりにはなりません"),
("whitelist_cidr_tip", "CIDR 表記に対応しています。例: 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2
View File
@@ -773,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "귀하의 IP가 상대방에 의해 차단되었습니다"),
("id_whitelist_caveat_tip", "ID는 연결하는 클라이언트가 보고합니다. 화이트리스트는 노출을 줄이는 것으로 비밀번호나 2FA를 대체하지 않습니다"),
("whitelist_cidr_tip", "CIDR 표기를 지원합니다. 예: 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Релай Қосылым"),
("Secure Connection", "Қауіпсіз Қосылым"),
("Insecure Connection", "Қатерлі Қосылым"),
("Continue", ""),
("Scale original", "Scale original"),
("Scale adaptive", "Scale adaptive"),
("General", "Жалпы"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Сіздің IP-мекенжайыңыз қарсы тараппен бұғатталған"),
("id_whitelist_caveat_tip", "ID қосылатын клиентпен хабарланады. Ақ-тізім әсер ету аумағын азайтады және құпия сөзді немесе 2FA-ны алмастырмайды"),
("whitelist_cidr_tip", "CIDR жазбасына қолдау көрсетіледі, мысалы 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Tarpinė jungtis"),
("Secure Connection", "Saugus ryšys"),
("Insecure Connection", "Nesaugus ryšys"),
("Continue", ""),
("Scale original", "Pakeisti originalų mastelį"),
("Scale adaptive", "Pritaikomas mastelis"),
("General", "Bendra"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Jūsų IP adresą užblokavo nuotolinis įrenginys"),
("id_whitelist_caveat_tip", "ID praneša prisijungiantis klientas. Šis sąrašas sumažina atakos paviršių ir nepakeičia slaptažodžio ar 2FA"),
("whitelist_cidr_tip", "Palaikomas CIDR žymėjimas, pavyzdžiui 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Releja savienojums"),
("Secure Connection", "Drošs savienojums"),
("Insecure Connection", "Nedrošs savienojums"),
("Continue", ""),
("Scale original", "Mērogs oriģināls"),
("Scale adaptive", "Mērogs adaptīvs"),
("General", "Vispārīgi"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Jūsu IP adresi ir bloķējusi otra puse"),
("id_whitelist_caveat_tip", "ID paziņo klients, kas veido savienojumu. Baltais saraksts samazina pakļautību un neaizstāj paroli vai 2FA"),
("whitelist_cidr_tip", "Tiek atbalstīts CIDR pieraksts, piemēram 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "റിലേ കണക്ഷൻ"),
("Secure Connection", "സുരക്ഷിതമായ കണക്ഷൻ"),
("Insecure Connection", "സുരക്ഷിതമല്ലാത്ത കണക്ഷൻ"),
("Continue", ""),
("Scale original", "ഒറിജിനൽ വലിപ്പം"),
("Scale adaptive", "അഡാപ്റ്റീവ് വലിപ്പം"),
("General", "പൊതുവായവ"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "നിങ്ങളുടെ IP വിലാസം മറുവശം ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"),
("id_whitelist_caveat_tip", "കണക്റ്റ് ചെയ്യുന്ന ക്ലയന്റാണ് ID റിപ്പോർട്ട് ചെയ്യുന്നത്. വൈറ്റ്‌ലിസ്റ്റ് എക്സ്പോഷർ കുറയ്ക്കുന്നു; പാസ്‌വേഡിനോ 2FA-യ്ക്കോ പകരമല്ല"),
("whitelist_cidr_tip", "CIDR നൊട്ടേഷൻ പിന്തുണയ്ക്കുന്നു, ഉദാഹരണത്തിന് 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Viderekoblet tilkobling"),
("Secure Connection", "Sikker tilkobling"),
("Insecure Connection", "Usikker tilkobling"),
("Continue", ""),
("Scale original", "Original skalering"),
("Scale adaptive", "Adaptiv skalering"),
("General", "Generelt"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP-adressen din er blokkert av motparten"),
("id_whitelist_caveat_tip", "ID-en rapporteres av klienten som kobler til. Hvitelisten reduserer eksponeringen og erstatter ikke passord eller 2FA"),
("whitelist_cidr_tip", "CIDR-notasjon støttes, for eksempel 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Relay-verbinding"),
("Secure Connection", "Beveiligde verbinding"),
("Insecure Connection", "Onveilige verbinding"),
("Continue", "Doorgaan"),
("Scale original", "Oorspronkelijk formaat"),
("Scale adaptive", "Automatisch schalen"),
("General", "Algemeen"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Uw IP wordt door de ander geblokkeerd"),
("id_whitelist_caveat_tip", "De ID wordt vermeld door de verbindende client. Deze witte lijst vermindert de zichtbaarheid en vervangt niet het wachtwoord of 2FA."),
("whitelist_cidr_tip", "CIDR-notatie wordt ondersteund, bijv. 192.168.1.0/24"),
("Continue", "Doorgaan"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Połączenie przez bramkę"),
("Secure Connection", "Połączenie szyfrowane"),
("Insecure Connection", "Połączenie nieszyfrowane"),
("Continue", "Kontynuuj"),
("Scale original", "Skalowanie oryginalne"),
("Scale adaptive", "Dopasuj do wyświetlacza"),
("General", "Ogólne"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Twój adres IP został zablokowany przez drugą stronę"),
("id_whitelist_caveat_tip", "ID jest zgłaszane przez łączącego się klienta. Biała lista zmniejsza ekspozycję i nie zastępuje hasła ani 2FA"),
("whitelist_cidr_tip", "Obsługiwana jest notacja CIDR, na przykład 192.168.1.0/24"),
("Continue", "Kontynuuj"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Conexão de relé"),
("Secure Connection", "Conexão segura"),
("Insecure Connection", "Conexão insegura"),
("Continue", ""),
("Scale original", "Escala original"),
("Scale adaptive", "Escala adaptável"),
("General", "Geral"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "O seu IP está bloqueado pelo dispositivo remoto"),
("id_whitelist_caveat_tip", "O ID é comunicado pelo cliente que se liga. A whitelist reduz a exposição e não substitui a palavra-passe nem o 2FA"),
("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Conexão via Relay"),
("Secure Connection", "Conexão Segura"),
("Insecure Connection", "Conexão Insegura"),
("Continue", "Continuar"),
("Scale original", "Escala original"),
("Scale adaptive", "Escala adaptada"),
("General", "Geral"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Seu IP foi bloqueado pelo dispositivo remoto"),
("id_whitelist_caveat_tip", "O ID é informado pelo cliente que se conecta. A lista reduz a exposição e não substitui a senha ou o 2FA"),
("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"),
("Continue", "Continuar"),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Conexiune prin retransmisie"),
("Secure Connection", "Conexiune securizată"),
("Insecure Connection", "Conexiune nesecurizată"),
("Continue", ""),
("Scale original", "Dimensiune originală"),
("Scale adaptive", "Scalare automată"),
("General", "General"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Adresa ta IP este blocată de dispozitivul de la distanță"),
("id_whitelist_caveat_tip", "ID-ul este raportat de clientul care se conectează. Lista albă reduce expunerea și nu înlocuiește parola sau 2FA"),
("whitelist_cidr_tip", "Notația CIDR este acceptată, de exemplu 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Ретранслируемое подключение"),
("Secure Connection", "Безопасное подключение"),
("Insecure Connection", "Небезопасное подключение"),
("Continue", ""),
("Scale original", "Оригинальный масштаб"),
("Scale adaptive", "Адаптивный масштаб"),
("General", "Общие"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Ваш IP-адрес заблокирован удалённым устройством"),
("id_whitelist_caveat_tip", "ID сообщается подключающимся клиентом. Белый список уменьшает поверхность атаки и не заменяет пароль или 2FA"),
("whitelist_cidr_tip", "Поддерживается нотация CIDR, например 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Connessione tramudada (relay)"),
("Secure Connection", "Connessione segura"),
("Insecure Connection", "Connessione non segura"),
("Continue", ""),
("Scale original", "Iscala originale"),
("Scale adaptive", "Iscala adativa"),
("General", "Generale"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "S'indiritzu IP tuo est blocadu dae s'àtera parte"),
("id_whitelist_caveat_tip", "S'ID est decraradu dae su cliente chi si connetet. Custu elencu minimat s'espositzione e non sostituit sa crae o su 2FA"),
("whitelist_cidr_tip", "Sa notatzione CIDR est suportada, pro esempru 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Reléové pripojenie"),
("Secure Connection", "Zabezpečené pripojenie"),
("Insecure Connection", "Nezabezpečené pripojenie"),
("Continue", ""),
("Scale original", "Pôvodná mierka"),
("Scale adaptive", "Prispôsobivá mierka"),
("General", "Všeobecné"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vaša IP adresa je blokovaná protistranou"),
("id_whitelist_caveat_tip", "ID nahlasuje pripájajúci sa klient. Tento zoznam znižuje vystavenie a nenahrádza heslo ani 2FA"),
("whitelist_cidr_tip", "Je podporovaný zápis CIDR, napríklad 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Posredovana povezava"),
("Secure Connection", "Zavarovana povezava"),
("Insecure Connection", "Nezavarovana povezava"),
("Continue", ""),
("Scale original", "Originalna velikost"),
("Scale adaptive", "Prilagojena velikost"),
("General", "Splošno"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vaš IP je blokirala oddaljena naprava"),
("id_whitelist_caveat_tip", "ID sporoči odjemalec, ki se povezuje. Seznam zmanjšuje izpostavljenost in ne nadomešča gesla ali 2FA"),
("whitelist_cidr_tip", "Podprt je zapis CIDR, na primer 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Lidhja rele"),
("Secure Connection", "Lidhje e sigurt"),
("Insecure Connection", "Lidhje e pasigurt"),
("Continue", ""),
("Scale original", "Shkalla origjinale"),
("Scale adaptive", " E përsjhtatshme në shkallë"),
("General", "Gjeneral"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP-ja juaj është bllokuar nga pala tjetër"),
("id_whitelist_caveat_tip", "ID-ja raportohet nga klienti që lidhet. Lista e bardhë zvogëlon ekspozimin dhe nuk zëvendëson fjalëkalimin ose 2FA"),
("whitelist_cidr_tip", "Mbështetet shënimi CIDR, për shembull 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Posredna konekcija"),
("Secure Connection", "Bezbedna konekcija"),
("Insecure Connection", "Nebezbedna konekcija"),
("Continue", ""),
("Scale original", "Skaliraj original"),
("Scale adaptive", "Adaptivno skaliranje"),
("General", "Uopšteno"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Vašu IP adresu je blokirala druga strana"),
("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamenjuje lozinku ni 2FA"),
("whitelist_cidr_tip", "Podržan je CIDR zapis, na primer 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Relayanslutning"),
("Secure Connection", "Säker anslutning"),
("Insecure Connection", "Osäker anslutning"),
("Continue", ""),
("Scale original", "Skala orginal"),
("Scale adaptive", "Skala adaptivt"),
("General", "Generellt"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Din IP-adress är blockerad av motparten"),
("id_whitelist_caveat_tip", "ID:t rapporteras av klienten som ansluter. Vitlistan minskar exponeringen och ersätter inte lösenord eller 2FA"),
("whitelist_cidr_tip", "CIDR-notation stöds, till exempel 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "ரிலே இணைப்பு"),
("Secure Connection", "பாதுகாப்பான இணைப்பு"),
("Insecure Connection", "பாதுகாப்பற்ற இணைப்பு"),
("Continue", ""),
("Scale original", "அசல் அளவு"),
("Scale adaptive", "தகவமைப்பு அளவு"),
("General", "பொது"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "உங்கள் IP முகவரி மறுமுனையால் தடுக்கப்பட்டுள்ளது"),
("id_whitelist_caveat_tip", "இணைக்கும் கிளையண்டே ID-ஐ தெரிவிக்கிறது. அனுமதிப்பட்டியல் வெளிப்பாட்டைக் குறைக்கிறது; கடவுச்சொல் அல்லது 2FA-க்கு மாற்றாகாது"),
("whitelist_cidr_tip", "CIDR குறியீடு ஆதரிக்கப்படுகிறது, எடுத்துக்காட்டாக 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", ""),
("Secure Connection", ""),
("Insecure Connection", ""),
("Continue", ""),
("Scale original", ""),
("Scale adaptive", ""),
("General", ""),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", ""),
("id_whitelist_caveat_tip", ""),
("whitelist_cidr_tip", ""),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "การเชื่อมต่อแบบ Relay "),
("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"),
("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"),
("Continue", ""),
("Scale original", "ขนาดเดิม"),
("Scale adaptive", "ขนาดยืดหยุ่น"),
("General", "ทั่วไป"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP ของคุณถูกบล็อกโดยฝั่งตรงข้าม"),
("id_whitelist_caveat_tip", "ID ถูกรายงานโดยไคลเอนต์ที่เชื่อมต่อ ไวท์ลิสต์ช่วยลดการเปิดเผยและไม่สามารถใช้แทนรหัสผ่านหรือ 2FA ได้"),
("whitelist_cidr_tip", "รองรับรูปแบบ CIDR เช่น 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Aktarmalı Bağlantı"),
("Secure Connection", "Güvenli Bağlantı"),
("Insecure Connection", "Güvenli Olmayan Bağlantı"),
("Continue", ""),
("Scale original", "Orijinal ölçekte"),
("Scale adaptive", "Uyarlanabilir ölçekte"),
("General", "Genel"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP adresiniz karşı taraf tarafından engellendi"),
("id_whitelist_caveat_tip", "ID, bağlanan istemci tarafından bildirilir. Bu liste maruziyeti azaltır; parolanın veya 2FA'nın yerini tutmaz"),
("whitelist_cidr_tip", "CIDR gösterimi desteklenir, örneğin 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "中繼連線"),
("Secure Connection", "安全連線"),
("Insecure Connection", "非安全連線"),
("Continue", ""),
("Scale original", "原始尺寸"),
("Scale adaptive", "適應視窗"),
("General", "一般"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "你的 IP 已被對方封鎖"),
("id_whitelist_caveat_tip", "ID 由對端用戶端回報,白名單用於減少暴露面,不能取代密碼或 2FA"),
("whitelist_cidr_tip", "支援 CIDR 寫法,例如 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Ретрансльоване підключення"),
("Secure Connection", "Безпечне підключення"),
("Insecure Connection", "Небезпечне підключення"),
("Continue", ""),
("Scale original", "Оригінальний масштаб"),
("Scale adaptive", "Адаптивний масштаб"),
("General", "Загальні"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "Вашу IP-адресу заблоковано віддаленим пристроєм"),
("id_whitelist_caveat_tip", "ID повідомляється клієнтом, що підключається. Білий список зменшує поверхню атаки і не замінює пароль або 2FA"),
("whitelist_cidr_tip", "Підтримується нотація CIDR, наприклад 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}
+2 -1
View File
@@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Relay Connection", "Kết nối chuyển tiếp"),
("Secure Connection", "Kết nối bảo mật"),
("Insecure Connection", "Kết nối không bảo mật"),
("Continue", ""),
("Scale original", "Tỷ lệ gốc"),
("Scale adaptive", "Tỷ lệ thích ứng"),
("General", "Chung"),
@@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Your ip is blocked by the peer", "IP của bạn đã bị phía bên kia chặn"),
("id_whitelist_caveat_tip", "ID do máy khách kết nối tự khai báo. Danh sách trắng giúp giảm mức độ lộ diện và không thay thế mật khẩu hay 2FA"),
("whitelist_cidr_tip", "Hỗ trợ ký hiệu CIDR, ví dụ 192.168.1.0/24"),
("Continue", ""),
("Browser didn't open? Use the url below to sign in.", ""),
].iter().cloned().collect();
}