mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 16:32:01 +03:00
Add isAdmin to RpcState.
This commit is contained in:
parent
6b43fb3799
commit
a864787329
@ -10,8 +10,11 @@ class AppFailureScreen extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: const [
|
||||
Text('Failed to connect'),
|
||||
children: [
|
||||
Text(
|
||||
reason,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/models.dart';
|
||||
import '../../desktop/state.dart';
|
||||
import '../models.dart';
|
||||
import 'device_avatar.dart';
|
||||
|
||||
@ -11,14 +12,15 @@ class NoDeviceScreen extends ConsumerWidget {
|
||||
final DeviceNode? node;
|
||||
const NoDeviceScreen(this.node, {Key? key}) : super(key: key);
|
||||
|
||||
String _getErrorMessage(UsbPid pid) {
|
||||
String _getErrorMessage(WidgetRef ref, UsbPid pid) {
|
||||
// TODO: Handle more cases
|
||||
if (pid.usbInterfaces == UsbInterface.fido.value) {
|
||||
if (Platform.isWindows) {
|
||||
// TODO: Only when not admin!
|
||||
if (!ref.watch(rpcStateProvider.select((state) => state.isAdmin))) {
|
||||
return 'WebAuthn management requires elevated privileges.\nRestart this app as administrator.';
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'This YubiKey cannot be accessed';
|
||||
}
|
||||
|
||||
@ -31,7 +33,7 @@ class NoDeviceScreen extends ConsumerWidget {
|
||||
return [
|
||||
const DeviceAvatar(child: Icon(Icons.usb_off)),
|
||||
Text(
|
||||
_getErrorMessage(node.pid),
|
||||
_getErrorMessage(ref, node.pid),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
];
|
||||
|
@ -16,5 +16,8 @@ class RpcResponse with _$RpcResponse {
|
||||
|
||||
@freezed
|
||||
class RpcState with _$RpcState {
|
||||
const factory RpcState(String version) = _RpcState;
|
||||
const factory RpcState(String version, bool isAdmin) = _RpcState;
|
||||
|
||||
factory RpcState.fromJson(Map<String, dynamic> json) =>
|
||||
_$RpcStateFromJson(json);
|
||||
}
|
||||
|
@ -651,15 +651,24 @@ abstract class RpcError implements RpcResponse {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
RpcState _$RpcStateFromJson(Map<String, dynamic> json) {
|
||||
return _RpcState.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$RpcStateTearOff {
|
||||
const _$RpcStateTearOff();
|
||||
|
||||
_RpcState call(String version) {
|
||||
_RpcState call(String version, bool isAdmin) {
|
||||
return _RpcState(
|
||||
version,
|
||||
isAdmin,
|
||||
);
|
||||
}
|
||||
|
||||
RpcState fromJson(Map<String, Object?> json) {
|
||||
return RpcState.fromJson(json);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -668,7 +677,9 @@ const $RpcState = _$RpcStateTearOff();
|
||||
/// @nodoc
|
||||
mixin _$RpcState {
|
||||
String get version => throw _privateConstructorUsedError;
|
||||
bool get isAdmin => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$RpcStateCopyWith<RpcState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -678,7 +689,7 @@ mixin _$RpcState {
|
||||
abstract class $RpcStateCopyWith<$Res> {
|
||||
factory $RpcStateCopyWith(RpcState value, $Res Function(RpcState) then) =
|
||||
_$RpcStateCopyWithImpl<$Res>;
|
||||
$Res call({String version});
|
||||
$Res call({String version, bool isAdmin});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -692,12 +703,17 @@ class _$RpcStateCopyWithImpl<$Res> implements $RpcStateCopyWith<$Res> {
|
||||
@override
|
||||
$Res call({
|
||||
Object? version = freezed,
|
||||
Object? isAdmin = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
version: version == freezed
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isAdmin: isAdmin == freezed
|
||||
? _value.isAdmin
|
||||
: isAdmin // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -707,7 +723,7 @@ abstract class _$RpcStateCopyWith<$Res> implements $RpcStateCopyWith<$Res> {
|
||||
factory _$RpcStateCopyWith(_RpcState value, $Res Function(_RpcState) then) =
|
||||
__$RpcStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({String version});
|
||||
$Res call({String version, bool isAdmin});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -722,27 +738,37 @@ class __$RpcStateCopyWithImpl<$Res> extends _$RpcStateCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? version = freezed,
|
||||
Object? isAdmin = freezed,
|
||||
}) {
|
||||
return _then(_RpcState(
|
||||
version == freezed
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isAdmin == freezed
|
||||
? _value.isAdmin
|
||||
: isAdmin // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
@JsonSerializable()
|
||||
class _$_RpcState implements _RpcState {
|
||||
const _$_RpcState(this.version);
|
||||
const _$_RpcState(this.version, this.isAdmin);
|
||||
|
||||
factory _$_RpcState.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_RpcStateFromJson(json);
|
||||
|
||||
@override
|
||||
final String version;
|
||||
@override
|
||||
final bool isAdmin;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RpcState(version: $version)';
|
||||
return 'RpcState(version: $version, isAdmin: $isAdmin)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -750,25 +776,37 @@ class _$_RpcState implements _RpcState {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _RpcState &&
|
||||
const DeepCollectionEquality().equals(other.version, version));
|
||||
const DeepCollectionEquality().equals(other.version, version) &&
|
||||
const DeepCollectionEquality().equals(other.isAdmin, isAdmin));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(version));
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(version),
|
||||
const DeepCollectionEquality().hash(isAdmin));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$RpcStateCopyWith<_RpcState> get copyWith =>
|
||||
__$RpcStateCopyWithImpl<_RpcState>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_RpcStateToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _RpcState implements RpcState {
|
||||
const factory _RpcState(String version) = _$_RpcState;
|
||||
const factory _RpcState(String version, bool isAdmin) = _$_RpcState;
|
||||
|
||||
factory _RpcState.fromJson(Map<String, dynamic> json) = _$_RpcState.fromJson;
|
||||
|
||||
@override
|
||||
String get version;
|
||||
@override
|
||||
bool get isAdmin;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$RpcStateCopyWith<_RpcState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
@ -42,3 +42,14 @@ Map<String, dynamic> _$$RpcErrorToJson(_$RpcError instance) =>
|
||||
'body': instance.body,
|
||||
'kind': instance.$type,
|
||||
};
|
||||
|
||||
_$_RpcState _$$_RpcStateFromJson(Map<String, dynamic> json) => _$_RpcState(
|
||||
json['version'] as String,
|
||||
json['is_admin'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_RpcStateToJson(_$_RpcState instance) =>
|
||||
<String, dynamic>{
|
||||
'version': instance.version,
|
||||
'is_admin': instance.isAdmin,
|
||||
};
|
||||
|
@ -31,14 +31,14 @@ final rpcStateProvider = StateNotifierProvider<_RpcStateNotifier, RpcState>(
|
||||
|
||||
class _RpcStateNotifier extends StateNotifier<RpcState> {
|
||||
final RpcSession rpc;
|
||||
_RpcStateNotifier(this.rpc) : super(const RpcState('unknown')) {
|
||||
_RpcStateNotifier(this.rpc) : super(const RpcState('unknown', false)) {
|
||||
_init();
|
||||
}
|
||||
|
||||
_init() async {
|
||||
final response = await rpc.command('get', []);
|
||||
if (mounted) {
|
||||
state = state.copyWith(version: response['data']['version']);
|
||||
state = RpcState.fromJson(response['data']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:yubico_authenticator/desktop/state.dart';
|
||||
|
||||
import '../../app/models.dart';
|
||||
import '../../app/views/app_failure_screen.dart';
|
||||
@ -14,7 +17,16 @@ class FidoScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) =>
|
||||
ref.watch(fidoStateProvider(deviceData.node.path)).when(
|
||||
none: () => const AppLoadingScreen(),
|
||||
failure: (reason) => AppFailureScreen(reason),
|
||||
failure: (reason) {
|
||||
if (Platform.isWindows) {
|
||||
if (!ref
|
||||
.watch(rpcStateProvider.select((state) => state.isAdmin))) {
|
||||
return const AppFailureScreen(
|
||||
'WebAuthn management requires elevated privileges.\nRestart this app as administrator.');
|
||||
}
|
||||
}
|
||||
return AppFailureScreen(reason);
|
||||
},
|
||||
success: (state) => ListView(
|
||||
children: [
|
||||
Text('${state.info}'),
|
||||
|
@ -55,11 +55,19 @@ from dataclasses import asdict
|
||||
from typing import Mapping, Tuple
|
||||
|
||||
import os
|
||||
import sys
|
||||
import ctypes
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_admin():
|
||||
if sys.platform == "win32":
|
||||
return bool(ctypes.windll.shell32.IsUserAnAdmin())
|
||||
return os.getuid() == 0
|
||||
|
||||
|
||||
class RootNode(RpcNode):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@ -78,7 +86,7 @@ class RootNode(RpcNode):
|
||||
return self._child
|
||||
|
||||
def get_data(self):
|
||||
return dict(version=ykman_version)
|
||||
return dict(version=ykman_version, is_admin=_is_admin())
|
||||
|
||||
@child
|
||||
def usb(self):
|
||||
|
Loading…
Reference in New Issue
Block a user