From 7d88c5c6a583a4e3f2ade6d5aafc52daccb2cfcd Mon Sep 17 00:00:00 2001 From: Dain Nilsson Date: Wed, 12 Jan 2022 12:49:04 +0100 Subject: [PATCH] Add basic NFC support. --- lib/app/models.dart | 21 +- lib/app/models.freezed.dart | 674 ++++++++++++++++++++----- lib/app/models.g.dart | 30 -- lib/app/state.dart | 210 ++++++-- lib/app/views/device_avatar.dart | 11 +- lib/app/views/device_images.dart | 10 +- lib/app/views/device_info_screen.dart | 2 +- lib/app/views/main_actions_dialog.dart | 143 ++++-- lib/app/views/main_page.dart | 11 +- lib/core/models.freezed.dart | 33 +- lib/management/models.freezed.dart | 51 +- lib/oath/models.freezed.dart | 106 ++-- lib/oath/views/account_list.dart | 2 +- lib/oath/views/account_view.dart | 5 +- lib/oath/views/oath_screen.dart | 8 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- yubikey-manager | 2 +- 18 files changed, 977 insertions(+), 348 deletions(-) delete mode 100755 lib/app/models.g.dart diff --git a/lib/app/models.dart b/lib/app/models.dart index 7577afe6..b454701d 100755 --- a/lib/app/models.dart +++ b/lib/app/models.dart @@ -3,10 +3,25 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import '../../management/models.dart'; part 'models.freezed.dart'; -part 'models.g.dart'; +//part 'models.g.dart'; enum SubPage { authenticator, yubikey } +@freezed +class YubiKeyData with _$YubiKeyData { + factory YubiKeyData(DeviceNode node, String name, DeviceInfo info) = + _YubiKeyData; +} + +@freezed +class DeviceNode with _$DeviceNode { + factory DeviceNode.usbYubiKey( + List path, String name, int pid, DeviceInfo info) = + UsbYubiKeyNode; + factory DeviceNode.nfcReader(List path, String name) = NfcReaderNode; +} + +/* @freezed class DeviceNode with _$DeviceNode { factory DeviceNode( @@ -16,10 +31,8 @@ class DeviceNode with _$DeviceNode { String name, DeviceInfo info, ) = _DeviceNode; - - factory DeviceNode.fromJson(Map json) => - _$DeviceNodeFromJson(json); } +*/ @freezed class MenuAction with _$MenuAction { diff --git a/lib/app/models.freezed.dart b/lib/app/models.freezed.dart index 3daade68..c24a5bbe 100755 --- a/lib/app/models.freezed.dart +++ b/lib/app/models.freezed.dart @@ -1,5 +1,6 @@ // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target part of 'models.dart'; @@ -13,27 +14,212 @@ T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); -DeviceNode _$DeviceNodeFromJson(Map json) { - return _DeviceNode.fromJson(json); +/// @nodoc +class _$YubiKeyDataTearOff { + const _$YubiKeyDataTearOff(); + + _YubiKeyData call(DeviceNode node, String name, DeviceInfo info) { + return _YubiKeyData( + node, + name, + info, + ); + } +} + +/// @nodoc +const $YubiKeyData = _$YubiKeyDataTearOff(); + +/// @nodoc +mixin _$YubiKeyData { + DeviceNode get node => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + DeviceInfo get info => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $YubiKeyDataCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $YubiKeyDataCopyWith<$Res> { + factory $YubiKeyDataCopyWith( + YubiKeyData value, $Res Function(YubiKeyData) then) = + _$YubiKeyDataCopyWithImpl<$Res>; + $Res call({DeviceNode node, String name, DeviceInfo info}); + + $DeviceNodeCopyWith<$Res> get node; + $DeviceInfoCopyWith<$Res> get info; +} + +/// @nodoc +class _$YubiKeyDataCopyWithImpl<$Res> implements $YubiKeyDataCopyWith<$Res> { + _$YubiKeyDataCopyWithImpl(this._value, this._then); + + final YubiKeyData _value; + // ignore: unused_field + final $Res Function(YubiKeyData) _then; + + @override + $Res call({ + Object? node = freezed, + Object? name = freezed, + Object? info = freezed, + }) { + return _then(_value.copyWith( + node: node == freezed + ? _value.node + : node // ignore: cast_nullable_to_non_nullable + as DeviceNode, + name: name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + info: info == freezed + ? _value.info + : info // ignore: cast_nullable_to_non_nullable + as DeviceInfo, + )); + } + + @override + $DeviceNodeCopyWith<$Res> get node { + return $DeviceNodeCopyWith<$Res>(_value.node, (value) { + return _then(_value.copyWith(node: value)); + }); + } + + @override + $DeviceInfoCopyWith<$Res> get info { + return $DeviceInfoCopyWith<$Res>(_value.info, (value) { + return _then(_value.copyWith(info: value)); + }); + } +} + +/// @nodoc +abstract class _$YubiKeyDataCopyWith<$Res> + implements $YubiKeyDataCopyWith<$Res> { + factory _$YubiKeyDataCopyWith( + _YubiKeyData value, $Res Function(_YubiKeyData) then) = + __$YubiKeyDataCopyWithImpl<$Res>; + @override + $Res call({DeviceNode node, String name, DeviceInfo info}); + + @override + $DeviceNodeCopyWith<$Res> get node; + @override + $DeviceInfoCopyWith<$Res> get info; +} + +/// @nodoc +class __$YubiKeyDataCopyWithImpl<$Res> extends _$YubiKeyDataCopyWithImpl<$Res> + implements _$YubiKeyDataCopyWith<$Res> { + __$YubiKeyDataCopyWithImpl( + _YubiKeyData _value, $Res Function(_YubiKeyData) _then) + : super(_value, (v) => _then(v as _YubiKeyData)); + + @override + _YubiKeyData get _value => super._value as _YubiKeyData; + + @override + $Res call({ + Object? node = freezed, + Object? name = freezed, + Object? info = freezed, + }) { + return _then(_YubiKeyData( + node == freezed + ? _value.node + : node // ignore: cast_nullable_to_non_nullable + as DeviceNode, + name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + info == freezed + ? _value.info + : info // ignore: cast_nullable_to_non_nullable + as DeviceInfo, + )); + } +} + +/// @nodoc + +class _$_YubiKeyData implements _YubiKeyData { + _$_YubiKeyData(this.node, this.name, this.info); + + @override + final DeviceNode node; + @override + final String name; + @override + final DeviceInfo info; + + @override + String toString() { + return 'YubiKeyData(node: $node, name: $name, info: $info)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _YubiKeyData && + const DeepCollectionEquality().equals(other.node, node) && + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.info, info)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(node), + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(info)); + + @JsonKey(ignore: true) + @override + _$YubiKeyDataCopyWith<_YubiKeyData> get copyWith => + __$YubiKeyDataCopyWithImpl<_YubiKeyData>(this, _$identity); +} + +abstract class _YubiKeyData implements YubiKeyData { + factory _YubiKeyData(DeviceNode node, String name, DeviceInfo info) = + _$_YubiKeyData; + + @override + DeviceNode get node; + @override + String get name; + @override + DeviceInfo get info; + @override + @JsonKey(ignore: true) + _$YubiKeyDataCopyWith<_YubiKeyData> get copyWith => + throw _privateConstructorUsedError; } /// @nodoc class _$DeviceNodeTearOff { const _$DeviceNodeTearOff(); - _DeviceNode call(List path, int pid, Transport transport, String name, - DeviceInfo info) { - return _DeviceNode( + UsbYubiKeyNode usbYubiKey( + List path, String name, int pid, DeviceInfo info) { + return UsbYubiKeyNode( path, - pid, - transport, name, + pid, info, ); } - DeviceNode fromJson(Map json) { - return DeviceNode.fromJson(json); + NfcReaderNode nfcReader(List path, String name) { + return NfcReaderNode( + path, + name, + ); } } @@ -43,12 +229,51 @@ const $DeviceNode = _$DeviceNodeTearOff(); /// @nodoc mixin _$DeviceNode { List get path => throw _privateConstructorUsedError; - int get pid => throw _privateConstructorUsedError; - Transport get transport => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; - DeviceInfo get info => throw _privateConstructorUsedError; - Map toJson() => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function( + List path, String name, int pid, DeviceInfo info) + usbYubiKey, + required TResult Function(List path, String name) nfcReader, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(UsbYubiKeyNode value) usbYubiKey, + required TResult Function(NfcReaderNode value) nfcReader, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @JsonKey(ignore: true) $DeviceNodeCopyWith get copyWith => throw _privateConstructorUsedError; @@ -59,14 +284,7 @@ abstract class $DeviceNodeCopyWith<$Res> { factory $DeviceNodeCopyWith( DeviceNode value, $Res Function(DeviceNode) then) = _$DeviceNodeCopyWithImpl<$Res>; - $Res call( - {List path, - int pid, - Transport transport, - String name, - DeviceInfo info}); - - $DeviceInfoCopyWith<$Res> get info; + $Res call({List path, String name}); } /// @nodoc @@ -80,29 +298,64 @@ class _$DeviceNodeCopyWithImpl<$Res> implements $DeviceNodeCopyWith<$Res> { @override $Res call({ Object? path = freezed, - Object? pid = freezed, - Object? transport = freezed, Object? name = freezed, - Object? info = freezed, }) { return _then(_value.copyWith( path: path == freezed ? _value.path : path // ignore: cast_nullable_to_non_nullable as List, - pid: pid == freezed - ? _value.pid - : pid // ignore: cast_nullable_to_non_nullable - as int, - transport: transport == freezed - ? _value.transport - : transport // ignore: cast_nullable_to_non_nullable - as Transport, name: name == freezed ? _value.name : name // ignore: cast_nullable_to_non_nullable as String, - info: info == freezed + )); + } +} + +/// @nodoc +abstract class $UsbYubiKeyNodeCopyWith<$Res> + implements $DeviceNodeCopyWith<$Res> { + factory $UsbYubiKeyNodeCopyWith( + UsbYubiKeyNode value, $Res Function(UsbYubiKeyNode) then) = + _$UsbYubiKeyNodeCopyWithImpl<$Res>; + @override + $Res call({List path, String name, int pid, DeviceInfo info}); + + $DeviceInfoCopyWith<$Res> get info; +} + +/// @nodoc +class _$UsbYubiKeyNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res> + implements $UsbYubiKeyNodeCopyWith<$Res> { + _$UsbYubiKeyNodeCopyWithImpl( + UsbYubiKeyNode _value, $Res Function(UsbYubiKeyNode) _then) + : super(_value, (v) => _then(v as UsbYubiKeyNode)); + + @override + UsbYubiKeyNode get _value => super._value as UsbYubiKeyNode; + + @override + $Res call({ + Object? path = freezed, + Object? name = freezed, + Object? pid = freezed, + Object? info = freezed, + }) { + return _then(UsbYubiKeyNode( + path == freezed + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as List, + name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + pid == freezed + ? _value.pid + : pid // ignore: cast_nullable_to_non_nullable + as int, + info == freezed ? _value.info : info // ignore: cast_nullable_to_non_nullable as DeviceInfo, @@ -118,137 +371,282 @@ class _$DeviceNodeCopyWithImpl<$Res> implements $DeviceNodeCopyWith<$Res> { } /// @nodoc -abstract class _$DeviceNodeCopyWith<$Res> implements $DeviceNodeCopyWith<$Res> { - factory _$DeviceNodeCopyWith( - _DeviceNode value, $Res Function(_DeviceNode) then) = - __$DeviceNodeCopyWithImpl<$Res>; - @override - $Res call( - {List path, - int pid, - Transport transport, - String name, - DeviceInfo info}); - @override - $DeviceInfoCopyWith<$Res> get info; -} - -/// @nodoc -class __$DeviceNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res> - implements _$DeviceNodeCopyWith<$Res> { - __$DeviceNodeCopyWithImpl( - _DeviceNode _value, $Res Function(_DeviceNode) _then) - : super(_value, (v) => _then(v as _DeviceNode)); - - @override - _DeviceNode get _value => super._value as _DeviceNode; - - @override - $Res call({ - Object? path = freezed, - Object? pid = freezed, - Object? transport = freezed, - Object? name = freezed, - Object? info = freezed, - }) { - return _then(_DeviceNode( - path == freezed - ? _value.path - : path // ignore: cast_nullable_to_non_nullable - as List, - pid == freezed - ? _value.pid - : pid // ignore: cast_nullable_to_non_nullable - as int, - transport == freezed - ? _value.transport - : transport // ignore: cast_nullable_to_non_nullable - as Transport, - name == freezed - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - info == freezed - ? _value.info - : info // ignore: cast_nullable_to_non_nullable - as DeviceInfo, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_DeviceNode implements _DeviceNode { - _$_DeviceNode(this.path, this.pid, this.transport, this.name, this.info); - - factory _$_DeviceNode.fromJson(Map json) => - _$$_DeviceNodeFromJson(json); +class _$UsbYubiKeyNode implements UsbYubiKeyNode { + _$UsbYubiKeyNode(this.path, this.name, this.pid, this.info); @override final List path; @override - final int pid; - @override - final Transport transport; - @override final String name; @override + final int pid; + @override final DeviceInfo info; @override String toString() { - return 'DeviceNode(path: $path, pid: $pid, transport: $transport, name: $name, info: $info)'; + return 'DeviceNode.usbYubiKey(path: $path, name: $name, pid: $pid, info: $info)'; } @override bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _DeviceNode && + other is UsbYubiKeyNode && const DeepCollectionEquality().equals(other.path, path) && - (identical(other.pid, pid) || other.pid == pid) && - (identical(other.transport, transport) || - other.transport == transport) && - (identical(other.name, name) || other.name == name) && - (identical(other.info, info) || other.info == info)); + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.pid, pid) && + const DeepCollectionEquality().equals(other.info, info)); } @override - int get hashCode => Object.hash(runtimeType, - const DeepCollectionEquality().hash(path), pid, transport, name, info); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(path), + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(pid), + const DeepCollectionEquality().hash(info)); @JsonKey(ignore: true) @override - _$DeviceNodeCopyWith<_DeviceNode> get copyWith => - __$DeviceNodeCopyWithImpl<_DeviceNode>(this, _$identity); + $UsbYubiKeyNodeCopyWith get copyWith => + _$UsbYubiKeyNodeCopyWithImpl(this, _$identity); @override - Map toJson() { - return _$$_DeviceNodeToJson(this); + @optionalTypeArgs + TResult when({ + required TResult Function( + List path, String name, int pid, DeviceInfo info) + usbYubiKey, + required TResult Function(List path, String name) nfcReader, + }) { + return usbYubiKey(path, name, pid, info); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + }) { + return usbYubiKey?.call(path, name, pid, info); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + required TResult orElse(), + }) { + if (usbYubiKey != null) { + return usbYubiKey(path, name, pid, info); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UsbYubiKeyNode value) usbYubiKey, + required TResult Function(NfcReaderNode value) nfcReader, + }) { + return usbYubiKey(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + }) { + return usbYubiKey?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + required TResult orElse(), + }) { + if (usbYubiKey != null) { + return usbYubiKey(this); + } + return orElse(); } } -abstract class _DeviceNode implements DeviceNode { - factory _DeviceNode(List path, int pid, Transport transport, - String name, DeviceInfo info) = _$_DeviceNode; - - factory _DeviceNode.fromJson(Map json) = - _$_DeviceNode.fromJson; +abstract class UsbYubiKeyNode implements DeviceNode { + factory UsbYubiKeyNode( + List path, String name, int pid, DeviceInfo info) = + _$UsbYubiKeyNode; @override List get path; @override - int get pid; - @override - Transport get transport; - @override String get name; - @override + int get pid; DeviceInfo get info; @override @JsonKey(ignore: true) - _$DeviceNodeCopyWith<_DeviceNode> get copyWith => + $UsbYubiKeyNodeCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $NfcReaderNodeCopyWith<$Res> + implements $DeviceNodeCopyWith<$Res> { + factory $NfcReaderNodeCopyWith( + NfcReaderNode value, $Res Function(NfcReaderNode) then) = + _$NfcReaderNodeCopyWithImpl<$Res>; + @override + $Res call({List path, String name}); +} + +/// @nodoc +class _$NfcReaderNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res> + implements $NfcReaderNodeCopyWith<$Res> { + _$NfcReaderNodeCopyWithImpl( + NfcReaderNode _value, $Res Function(NfcReaderNode) _then) + : super(_value, (v) => _then(v as NfcReaderNode)); + + @override + NfcReaderNode get _value => super._value as NfcReaderNode; + + @override + $Res call({ + Object? path = freezed, + Object? name = freezed, + }) { + return _then(NfcReaderNode( + path == freezed + ? _value.path + : path // ignore: cast_nullable_to_non_nullable + as List, + name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$NfcReaderNode implements NfcReaderNode { + _$NfcReaderNode(this.path, this.name); + + @override + final List path; + @override + final String name; + + @override + String toString() { + return 'DeviceNode.nfcReader(path: $path, name: $name)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is NfcReaderNode && + const DeepCollectionEquality().equals(other.path, path) && + const DeepCollectionEquality().equals(other.name, name)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(path), + const DeepCollectionEquality().hash(name)); + + @JsonKey(ignore: true) + @override + $NfcReaderNodeCopyWith get copyWith => + _$NfcReaderNodeCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + List path, String name, int pid, DeviceInfo info) + usbYubiKey, + required TResult Function(List path, String name) nfcReader, + }) { + return nfcReader(path, name); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + }) { + return nfcReader?.call(path, name); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(List path, String name, int pid, DeviceInfo info)? + usbYubiKey, + TResult Function(List path, String name)? nfcReader, + required TResult orElse(), + }) { + if (nfcReader != null) { + return nfcReader(path, name); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(UsbYubiKeyNode value) usbYubiKey, + required TResult Function(NfcReaderNode value) nfcReader, + }) { + return nfcReader(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + }) { + return nfcReader?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(UsbYubiKeyNode value)? usbYubiKey, + TResult Function(NfcReaderNode value)? nfcReader, + required TResult orElse(), + }) { + if (nfcReader != null) { + return nfcReader(this); + } + return orElse(); + } +} + +abstract class NfcReaderNode implements DeviceNode { + factory NfcReaderNode(List path, String name) = _$NfcReaderNode; + + @override + List get path; + @override + String get name; + @override + @JsonKey(ignore: true) + $NfcReaderNodeCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -383,13 +781,17 @@ class _$_MenuAction implements _MenuAction { return identical(this, other) || (other.runtimeType == runtimeType && other is _MenuAction && - (identical(other.text, text) || other.text == text) && - (identical(other.icon, icon) || other.icon == icon) && + const DeepCollectionEquality().equals(other.text, text) && + const DeepCollectionEquality().equals(other.icon, icon) && (identical(other.action, action) || other.action == action)); } @override - int get hashCode => Object.hash(runtimeType, text, icon, action); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(text), + const DeepCollectionEquality().hash(icon), + action); @JsonKey(ignore: true) @override @@ -548,13 +950,17 @@ class _$_WindowState implements _WindowState { return identical(this, other) || (other.runtimeType == runtimeType && other is _WindowState && - (identical(other.focused, focused) || other.focused == focused) && - (identical(other.visible, visible) || other.visible == visible) && - (identical(other.active, active) || other.active == active)); + const DeepCollectionEquality().equals(other.focused, focused) && + const DeepCollectionEquality().equals(other.visible, visible) && + const DeepCollectionEquality().equals(other.active, active)); } @override - int get hashCode => Object.hash(runtimeType, focused, visible, active); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(focused), + const DeepCollectionEquality().hash(visible), + const DeepCollectionEquality().hash(active)); @JsonKey(ignore: true) @override diff --git a/lib/app/models.g.dart b/lib/app/models.g.dart deleted file mode 100755 index e0553306..00000000 --- a/lib/app/models.g.dart +++ /dev/null @@ -1,30 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'models.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_DeviceNode _$$_DeviceNodeFromJson(Map json) => - _$_DeviceNode( - (json['path'] as List).map((e) => e as String).toList(), - json['pid'] as int, - $enumDecode(_$TransportEnumMap, json['transport']), - json['name'] as String, - DeviceInfo.fromJson(json['info'] as Map), - ); - -Map _$$_DeviceNodeToJson(_$_DeviceNode instance) => - { - 'path': instance.path, - 'pid': instance.pid, - 'transport': _$TransportEnumMap[instance.transport], - 'name': instance.name, - 'info': instance.info, - }; - -const _$TransportEnumMap = { - Transport.usb: 'usb', - Transport.nfc: 'nfc', -}; diff --git a/lib/app/state.dart b/lib/app/state.dart index 0573e705..3cc1422a 100755 --- a/lib/app/state.dart +++ b/lib/app/state.dart @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; +import 'package:yubico_authenticator/management/models.dart'; import '../core/models.dart'; import '../core/state.dart'; @@ -118,24 +119,24 @@ class SearchNotifier extends StateNotifier { } } -final attachedDevicesProvider = - StateNotifierProvider>((ref) { - final notifier = AttachedDeviceNotifier(ref.watch(rpcProvider)); +final _usbDevicesProvider = + StateNotifierProvider>((ref) { + final notifier = UsbDeviceNotifier(ref.watch(rpcProvider)); ref.listen(windowStateProvider, (_, windowState) { notifier._notifyWindowState(windowState); }, fireImmediately: true); return notifier; }); -class AttachedDeviceNotifier extends StateNotifier> { +class UsbDeviceNotifier extends StateNotifier> { final RpcSession _rpc; Timer? _pollTimer; int _usbState = -1; - AttachedDeviceNotifier(this._rpc) : super([]); + UsbDeviceNotifier(this._rpc) : super([]); void _notifyWindowState(WindowState windowState) { if (windowState.active) { - _pollUsb(); + _pollDevices(); } else { _pollTimer?.cancel(); // Release any held device @@ -149,37 +150,110 @@ class AttachedDeviceNotifier extends StateNotifier> { super.dispose(); } - void _pollUsb() async { + void _pollDevices() async { _pollTimer?.cancel(); + try { var scan = await _rpc.command('scan', ['usb']); if (_usbState != scan['state'] || state.length != scan['pids'].length) { var usbResult = await _rpc.command('get', ['usb']); log.info('USB state change', jsonEncode(usbResult)); + _usbState = usbResult['data']['state']; + List usbDevices = []; - List devices = []; for (String id in (usbResult['children'] as Map).keys) { var path = ['usb', id]; var deviceResult = await _rpc.command('get', path); - devices.add( - DeviceNode.fromJson({'path': path, ...deviceResult['data']})); + var deviceData = deviceResult['data']; + usbDevices.add(DeviceNode.usbYubiKey( + path, + deviceData['name'], + deviceData['pid'], + DeviceInfo.fromJson(deviceData['info']), + ) as UsbYubiKeyNode); } - _usbState = usbResult['data']['state']; + log.info('USB state updated'); if (mounted) { - state = devices; + state = usbDevices; } } } on RpcError catch (e) { log.severe('Error polling USB', jsonEncode(e)); } + if (mounted) { - _pollTimer = Timer(const Duration(milliseconds: 500), _pollUsb); + _pollTimer = Timer(const Duration(milliseconds: 500), _pollDevices); } } } +final _nfcDevicesProvider = + StateNotifierProvider>((ref) { + final notifier = NfcDeviceNotifier(ref.watch(rpcProvider)); + ref.listen(windowStateProvider, (_, windowState) { + notifier._notifyWindowState(windowState); + }, fireImmediately: true); + return notifier; +}); + +class NfcDeviceNotifier extends StateNotifier> { + final RpcSession _rpc; + Timer? _pollTimer; + String _nfcState = ''; + NfcDeviceNotifier(this._rpc) : super([]); + + void _notifyWindowState(WindowState windowState) { + if (windowState.active) { + _pollReaders(); + } else { + _pollTimer?.cancel(); + // Release any held device + _rpc.command('get', ['nfc']); + } + } + + @override + void dispose() { + _pollTimer?.cancel(); + super.dispose(); + } + + void _pollReaders() async { + _pollTimer?.cancel(); + + try { + var children = await _rpc.command('scan', ['nfc']); + var newState = children.keys.join(':'); + + if (mounted && newState != _nfcState) { + log.info('NFC state change', jsonEncode(children)); + _nfcState = newState; + state = children.entries + .map((e) => + DeviceNode.nfcReader(['nfc', e.key], e.value['name'] as String) + as NfcReaderNode) + .toList(); + } + } on RpcError catch (e) { + log.severe('Error polling NFC', jsonEncode(e)); + } + + if (mounted) { + _pollTimer = Timer(const Duration(milliseconds: 2500), _pollReaders); + } + } +} + +final attachedDevicesProvider = Provider>((ref) { + final usbDevices = ref.watch(_usbDevicesProvider).toList(); + final nfcDevices = ref.watch(_nfcDevicesProvider).toList(); + usbDevices.sort((a, b) => a.name.compareTo(b.name)); + nfcDevices.sort((a, b) => a.name.compareTo(b.name)); + return [...usbDevices, ...nfcDevices]; +}); + final currentDeviceProvider = StateNotifierProvider((ref) { final provider = CurrentDeviceNotifier(ref.watch(prefProvider)); @@ -188,42 +262,110 @@ final currentDeviceProvider = }); class CurrentDeviceNotifier extends StateNotifier { - static const String _lastDeviceSerial = 'APP_STATE_LAST_SERIAL'; + static const String _lastDevice = 'APP_STATE_LAST_DEVICE'; final SharedPreferences _prefs; CurrentDeviceNotifier(this._prefs) : super(null); _updateAttachedDevices(List? previous, List devices) { - if (devices.isEmpty) { - state = null; - } else if (!devices.contains(state)) { - // Prefer last selected device - final serial = _prefs.getInt(_lastDeviceSerial) ?? -1; - state = devices.firstWhere( - (element) => element.info.serial == serial, - orElse: () => devices.first, - ); + if (!devices.contains(state)) { + final lastDevice = _prefs.getString(_lastDevice) ?? ''; + try { + state = devices.firstWhere( + (dev) => dev.when( + usbYubiKey: (path, name, pid, info) => + lastDevice == 'serial:${info.serial}', + nfcReader: (path, name) => lastDevice == 'name:$name', + ), + orElse: () => devices.whereType().first); + } on StateError { + state = null; + } } } setCurrentDevice(DeviceNode device) { state = device; - final serial = device.info.serial; - if (serial != null) { - _prefs.setInt(_lastDeviceSerial, serial); - } + device.when( + usbYubiKey: (path, name, pid, info) { + final serial = info.serial; + if (serial != null) { + _prefs.setString(_lastDevice, 'serial:$serial'); + } + }, + nfcReader: (path, name) { + _prefs.setString(_lastDevice, 'name:$name'); + }, + ); } } -final sortedDevicesProvider = Provider>((ref) { - final devices = ref.watch(attachedDevicesProvider).toList(); - devices.sort((a, b) => a.name.compareTo(b.name)); - final device = ref.watch(currentDeviceProvider); - if (device != null) { - return [device, ...devices.where((e) => e != device)]; +final currentDeviceDataProvider = + StateNotifierProvider((ref) { + final notifier = CurrentDeviceDataNotifier( + ref.watch(rpcProvider), + ref.watch(currentDeviceProvider), + ); + if (notifier._deviceNode is NfcReaderNode) { + // If this is an NFC reader, listen on WindowState. + ref.listen(windowStateProvider, (_, windowState) { + notifier._notifyWindowState(windowState); + }, fireImmediately: true); } - return devices; + return notifier; }); +class CurrentDeviceDataNotifier extends StateNotifier { + final RpcSession _rpc; + final DeviceNode? _deviceNode; + Timer? _pollTimer; + + CurrentDeviceDataNotifier(this._rpc, this._deviceNode) : super(null) { + final dev = _deviceNode; + if (dev is UsbYubiKeyNode) { + state = YubiKeyData(dev, dev.name, dev.info); + } + } + + void _notifyWindowState(WindowState windowState) { + if (windowState.active) { + _pollReader(); + } else { + _pollTimer?.cancel(); + // TODO: Should we clear the key here? + /*if (mounted) { + state = null; + }*/ + } + } + + @override + void dispose() { + _pollTimer?.cancel(); + super.dispose(); + } + + void _pollReader() async { + _pollTimer?.cancel(); + final node = _deviceNode!; + try { + var result = await _rpc.command('get', node.path); + if (mounted) { + if (result['data']['present']) { + state = YubiKeyData(node, result['data']['name'], + DeviceInfo.fromJson(result['data']['info'])); + } else { + state = null; + } + } + } on RpcError catch (e) { + log.severe('Error polling NFC', jsonEncode(e)); + } + if (mounted) { + _pollTimer = Timer(Duration(seconds: state == null ? 1 : 5), _pollReader); + } + } +} + final subPageProvider = StateNotifierProvider( (ref) => SubPageNotifier(SubPage.authenticator)); diff --git a/lib/app/views/device_avatar.dart b/lib/app/views/device_avatar.dart index d57c05f2..fac8df21 100755 --- a/lib/app/views/device_avatar.dart +++ b/lib/app/views/device_avatar.dart @@ -1,20 +1,25 @@ import 'package:flutter/material.dart'; +import 'package:yubico_authenticator/management/models.dart'; import '../models.dart'; import 'device_images.dart'; class DeviceAvatar extends StatelessWidget { - final DeviceNode device; + final DeviceNode node; + final String name; + final DeviceInfo? info; final bool selected; - const DeviceAvatar(this.device, {this.selected = false, Key? key}) + const DeviceAvatar(this.node, this.name, this.info, + {this.selected = false, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return CircleAvatar( child: CircleAvatar( - child: getProductImage(device), + child: + info != null ? getProductImage(info!, name) : const Icon(Icons.nfc), backgroundColor: Theme.of(context).colorScheme.background, ), radius: 22, diff --git a/lib/app/views/device_images.dart b/lib/app/views/device_images.dart index 317f17d6..1a86ec25 100755 --- a/lib/app/views/device_images.dart +++ b/lib/app/views/device_images.dart @@ -30,11 +30,11 @@ const _imagesForFormFactorNfc = { FormFactor.usbCKeychain: 'yk5cnfc', }; -Image getProductImage(DeviceNode device) { - var image = _imagesForName[device.name]; - image ??= device.info.supportedCapabilities.containsKey(Transport.nfc) - ? _imagesForFormFactorNfc[device.info.formFactor] - : _imagesForFormFactor[device.info.formFactor]; +Image getProductImage(DeviceInfo info, String name) { + var image = _imagesForName[name]; + image ??= info.supportedCapabilities.containsKey(Transport.nfc) + ? _imagesForFormFactorNfc[info.formFactor] + : _imagesForFormFactor[info.formFactor]; image ??= 'yk5series'; return Image.asset('assets/product-images/$image.png'); diff --git a/lib/app/views/device_info_screen.dart b/lib/app/views/device_info_screen.dart index fbc07450..7a210c6a 100755 --- a/lib/app/views/device_info_screen.dart +++ b/lib/app/views/device_info_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import '../models.dart'; class DeviceInfoScreen extends StatelessWidget { - final DeviceNode device; + final YubiKeyData device; const DeviceInfoScreen(this.device, {Key? key}) : super(key: key); @override diff --git a/lib/app/views/main_actions_dialog.dart b/lib/app/views/main_actions_dialog.dart index a8e663ae..e00d137b 100755 --- a/lib/app/views/main_actions_dialog.dart +++ b/lib/app/views/main_actions_dialog.dart @@ -1,32 +1,56 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:yubico_authenticator/management/models.dart'; +import 'package:collection/collection.dart'; import '../models.dart'; import '../state.dart'; import 'device_avatar.dart'; +Function _listEquals = const ListEquality().equals; + class MainActionsDialog extends ConsumerWidget { const MainActionsDialog({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - final devices = ref.watch(sortedDevicesProvider); - final device = ref.watch(currentDeviceProvider); + final devices = ref.watch(attachedDevicesProvider).toList(); + final currentNode = ref.watch(currentDeviceProvider); + final data = ref.watch(currentDeviceDataProvider); final actions = ref.watch(menuActionsProvider)(context); + //final nfcReaders = ref.watch(nfcDevicesProvider); + + //final allDevices = devices + nfcReaders; + if (currentNode != null) { + devices.removeWhere((e) => _listEquals(e.path, currentNode.path)); + } return SimpleDialog( children: [ - ...devices.map((e) => Padding( - padding: const EdgeInsets.all(8.0), - child: DeviceRow( - e, - selected: e == device, - onPressed: () { - Navigator.of(context).pop(); - ref.read(currentDeviceProvider.notifier).setCurrentDevice(e); - }, - ), - )), + if (currentNode != null) + CurrentDeviceRow( + currentNode, + data?.name, + info: data?.info, + onTap: () { + Navigator.of(context).pop(); + }, + ), + ...devices.map( + (e) => DeviceRow( + e, + e.name, + info: e.when( + usbYubiKey: (path, name, pid, info) => info, + nfcReader: (path, name) => null, + ), + selected: false, + onTap: () { + Navigator.of(context).pop(); + ref.read(currentDeviceProvider.notifier).setCurrentDevice(e); + }, + ), + ), if (actions.isNotEmpty) const Divider(), ...actions.map((a) => ListTile( dense: true, @@ -42,43 +66,74 @@ class MainActionsDialog extends ConsumerWidget { } } -class DeviceRow extends StatelessWidget { - final DeviceNode device; - final bool selected; - final Function() onPressed; - const DeviceRow( - this.device, { - this.selected = false, - required this.onPressed, +class CurrentDeviceRow extends StatelessWidget { + final DeviceNode node; + final String? name; + final DeviceInfo? info; + final Function() onTap; + + const CurrentDeviceRow( + this.node, + this.name, { + required this.info, + required this.onTap, Key? key, }) : super(key: key); @override Widget build(BuildContext context) { - return TextButton( - onPressed: onPressed, - child: Row( - children: [ - DeviceAvatar( - device, - selected: selected, - ), - const SizedBox(width: 16.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - device.name, - style: Theme.of(context).textTheme.headline6, - ), - Text( - 'S/N: ${device.info.serial} F/W: ${device.info.version}', - style: Theme.of(context).textTheme.bodyText1, - ), - ], - ), - ], + final subtitle = node is NfcReaderNode + ? info != null + ? '${node.name}\nS/N: ${info!.serial} F/W: ${info!.version}' + : node.name + : 'S/N: ${info!.serial} F/W: ${info!.version}'; + return ListTile( + leading: DeviceAvatar( + node, + name ?? '', + info, + selected: true, ), + title: Text(name ?? 'No YubiKey present'), + isThreeLine: subtitle.contains('\n'), + subtitle: Text(subtitle), + onTap: onTap, + ); + } +} + +class DeviceRow extends StatelessWidget { + final DeviceNode node; + final String name; + final DeviceInfo? info; + final bool selected; + final Function() onTap; + + const DeviceRow( + this.node, + this.name, { + required this.info, + required this.onTap, + this.selected = false, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: DeviceAvatar( + node, + name, + info, + selected: selected, + ), + title: Text(name), + subtitle: Text( + info == null + ? (selected ? 'No YubiKey present' : 'Select to scan') + : 'S/N: ${info!.serial} F/W: ${info!.version}', + ), + onTap: onTap, ); } } diff --git a/lib/app/views/main_page.dart b/lib/app/views/main_page.dart index 4106e610..9c82c2a5 100755 --- a/lib/app/views/main_page.dart +++ b/lib/app/views/main_page.dart @@ -13,7 +13,7 @@ import '../../oath/views/oath_screen.dart'; class MainPage extends ConsumerWidget { const MainPage({Key? key}) : super(key: key); - Widget _buildSubPage(SubPage subPage, DeviceNode? device) { + Widget _buildSubPage(SubPage subPage, YubiKeyData? device) { if (device == null) { return const NoDeviceScreen(); } @@ -28,7 +28,7 @@ class MainPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final currentDevice = ref.watch(currentDeviceProvider); + final currentDevice = ref.watch(currentDeviceDataProvider); final subPage = ref.watch(subPageProvider); return Scaffold( @@ -67,7 +67,12 @@ class MainPage extends ConsumerWidget { color: Theme.of(context).colorScheme.background, ), ) - : DeviceAvatar(currentDevice, selected: true), + : DeviceAvatar( + currentDevice.node, + currentDevice.name, + currentDevice.info, + selected: true, + ), ), onTap: () { showDialog( diff --git a/lib/core/models.freezed.dart b/lib/core/models.freezed.dart index 77699340..1258e4f5 100755 --- a/lib/core/models.freezed.dart +++ b/lib/core/models.freezed.dart @@ -1,5 +1,6 @@ // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target part of 'models.dart'; @@ -134,13 +135,17 @@ class _$_Version extends _Version { return identical(this, other) || (other.runtimeType == runtimeType && other is _Version && - (identical(other.major, major) || other.major == major) && - (identical(other.minor, minor) || other.minor == minor) && - (identical(other.patch, patch) || other.patch == patch)); + const DeepCollectionEquality().equals(other.major, major) && + const DeepCollectionEquality().equals(other.minor, minor) && + const DeepCollectionEquality().equals(other.patch, patch)); } @override - int get hashCode => Object.hash(runtimeType, major, minor, patch); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(major), + const DeepCollectionEquality().hash(minor), + const DeepCollectionEquality().hash(patch)); @JsonKey(ignore: true) @override @@ -516,13 +521,15 @@ class _$Signal implements Signal { return identical(this, other) || (other.runtimeType == runtimeType && other is Signal && - (identical(other.status, status) || other.status == status) && + const DeepCollectionEquality().equals(other.status, status) && const DeepCollectionEquality().equals(other.body, body)); } @override int get hashCode => Object.hash( - runtimeType, status, const DeepCollectionEquality().hash(body)); + runtimeType, + const DeepCollectionEquality().hash(status), + const DeepCollectionEquality().hash(body)); @JsonKey(ignore: true) @override @@ -689,14 +696,17 @@ class _$RpcError implements RpcError { return identical(this, other) || (other.runtimeType == runtimeType && other is RpcError && - (identical(other.status, status) || other.status == status) && - (identical(other.message, message) || other.message == message) && + const DeepCollectionEquality().equals(other.status, status) && + const DeepCollectionEquality().equals(other.message, message) && const DeepCollectionEquality().equals(other.body, body)); } @override int get hashCode => Object.hash( - runtimeType, status, message, const DeepCollectionEquality().hash(body)); + runtimeType, + const DeepCollectionEquality().hash(status), + const DeepCollectionEquality().hash(message), + const DeepCollectionEquality().hash(body)); @JsonKey(ignore: true) @override @@ -896,11 +906,12 @@ class _$_RpcState implements _RpcState { return identical(this, other) || (other.runtimeType == runtimeType && other is _RpcState && - (identical(other.version, version) || other.version == version)); + const DeepCollectionEquality().equals(other.version, version)); } @override - int get hashCode => Object.hash(runtimeType, version); + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(version)); @JsonKey(ignore: true) @override diff --git a/lib/management/models.freezed.dart b/lib/management/models.freezed.dart index b2869c6f..249b2467 100755 --- a/lib/management/models.freezed.dart +++ b/lib/management/models.freezed.dart @@ -1,5 +1,6 @@ // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target part of 'models.dart'; @@ -183,22 +184,21 @@ class _$_DeviceConfig implements _DeviceConfig { other is _DeviceConfig && const DeepCollectionEquality() .equals(other.enabledCapabilities, enabledCapabilities) && - (identical(other.autoEjectTimeout, autoEjectTimeout) || - other.autoEjectTimeout == autoEjectTimeout) && - (identical( - other.challengeResponseTimeout, challengeResponseTimeout) || - other.challengeResponseTimeout == challengeResponseTimeout) && - (identical(other.deviceFlags, deviceFlags) || - other.deviceFlags == deviceFlags)); + const DeepCollectionEquality() + .equals(other.autoEjectTimeout, autoEjectTimeout) && + const DeepCollectionEquality().equals( + other.challengeResponseTimeout, challengeResponseTimeout) && + const DeepCollectionEquality() + .equals(other.deviceFlags, deviceFlags)); } @override int get hashCode => Object.hash( runtimeType, const DeepCollectionEquality().hash(enabledCapabilities), - autoEjectTimeout, - challengeResponseTimeout, - deviceFlags); + const DeepCollectionEquality().hash(autoEjectTimeout), + const DeepCollectionEquality().hash(challengeResponseTimeout), + const DeepCollectionEquality().hash(deviceFlags)); @JsonKey(ignore: true) @override @@ -495,30 +495,29 @@ class _$_DeviceInfo implements _DeviceInfo { return identical(this, other) || (other.runtimeType == runtimeType && other is _DeviceInfo && - (identical(other.config, config) || other.config == config) && - (identical(other.serial, serial) || other.serial == serial) && - (identical(other.version, version) || other.version == version) && - (identical(other.formFactor, formFactor) || - other.formFactor == formFactor) && + const DeepCollectionEquality().equals(other.config, config) && + const DeepCollectionEquality().equals(other.serial, serial) && + const DeepCollectionEquality().equals(other.version, version) && + const DeepCollectionEquality() + .equals(other.formFactor, formFactor) && const DeepCollectionEquality() .equals(other.supportedCapabilities, supportedCapabilities) && - (identical(other.isLocked, isLocked) || - other.isLocked == isLocked) && - (identical(other.isFips, isFips) || other.isFips == isFips) && - (identical(other.isSky, isSky) || other.isSky == isSky)); + const DeepCollectionEquality().equals(other.isLocked, isLocked) && + const DeepCollectionEquality().equals(other.isFips, isFips) && + const DeepCollectionEquality().equals(other.isSky, isSky)); } @override int get hashCode => Object.hash( runtimeType, - config, - serial, - version, - formFactor, + const DeepCollectionEquality().hash(config), + const DeepCollectionEquality().hash(serial), + const DeepCollectionEquality().hash(version), + const DeepCollectionEquality().hash(formFactor), const DeepCollectionEquality().hash(supportedCapabilities), - isLocked, - isFips, - isSky); + const DeepCollectionEquality().hash(isLocked), + const DeepCollectionEquality().hash(isFips), + const DeepCollectionEquality().hash(isSky)); @JsonKey(ignore: true) @override diff --git a/lib/oath/models.freezed.dart b/lib/oath/models.freezed.dart index a4a53c71..53e03384 100755 --- a/lib/oath/models.freezed.dart +++ b/lib/oath/models.freezed.dart @@ -1,5 +1,6 @@ // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target part of 'models.dart'; @@ -230,21 +231,26 @@ class _$_OathCredential implements _OathCredential { return identical(this, other) || (other.runtimeType == runtimeType && other is _OathCredential && - (identical(other.deviceId, deviceId) || - other.deviceId == deviceId) && - (identical(other.id, id) || other.id == id) && - (identical(other.issuer, issuer) || other.issuer == issuer) && - (identical(other.name, name) || other.name == name) && - (identical(other.oathType, oathType) || - other.oathType == oathType) && - (identical(other.period, period) || other.period == period) && - (identical(other.touchRequired, touchRequired) || - other.touchRequired == touchRequired)); + const DeepCollectionEquality().equals(other.deviceId, deviceId) && + const DeepCollectionEquality().equals(other.id, id) && + const DeepCollectionEquality().equals(other.issuer, issuer) && + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.oathType, oathType) && + const DeepCollectionEquality().equals(other.period, period) && + const DeepCollectionEquality() + .equals(other.touchRequired, touchRequired)); } @override int get hashCode => Object.hash( - runtimeType, deviceId, id, issuer, name, oathType, period, touchRequired); + runtimeType, + const DeepCollectionEquality().hash(deviceId), + const DeepCollectionEquality().hash(id), + const DeepCollectionEquality().hash(issuer), + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(oathType), + const DeepCollectionEquality().hash(period), + const DeepCollectionEquality().hash(touchRequired)); @JsonKey(ignore: true) @override @@ -429,14 +435,17 @@ class _$_OathCode implements _OathCode { return identical(this, other) || (other.runtimeType == runtimeType && other is _OathCode && - (identical(other.value, value) || other.value == value) && - (identical(other.validFrom, validFrom) || - other.validFrom == validFrom) && - (identical(other.validTo, validTo) || other.validTo == validTo)); + const DeepCollectionEquality().equals(other.value, value) && + const DeepCollectionEquality().equals(other.validFrom, validFrom) && + const DeepCollectionEquality().equals(other.validTo, validTo)); } @override - int get hashCode => Object.hash(runtimeType, value, validFrom, validTo); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(value), + const DeepCollectionEquality().hash(validFrom), + const DeepCollectionEquality().hash(validTo)); @JsonKey(ignore: true) @override @@ -605,13 +614,16 @@ class _$_OathPair implements _OathPair { return identical(this, other) || (other.runtimeType == runtimeType && other is _OathPair && - (identical(other.credential, credential) || - other.credential == credential) && - (identical(other.code, code) || other.code == code)); + const DeepCollectionEquality() + .equals(other.credential, credential) && + const DeepCollectionEquality().equals(other.code, code)); } @override - int get hashCode => Object.hash(runtimeType, credential, code); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(credential), + const DeepCollectionEquality().hash(code)); @JsonKey(ignore: true) @override @@ -772,14 +784,17 @@ class _$_OathState implements _OathState { return identical(this, other) || (other.runtimeType == runtimeType && other is _OathState && - (identical(other.deviceId, deviceId) || - other.deviceId == deviceId) && - (identical(other.hasKey, hasKey) || other.hasKey == hasKey) && - (identical(other.locked, locked) || other.locked == locked)); + const DeepCollectionEquality().equals(other.deviceId, deviceId) && + const DeepCollectionEquality().equals(other.hasKey, hasKey) && + const DeepCollectionEquality().equals(other.locked, locked)); } @override - int get hashCode => Object.hash(runtimeType, deviceId, hasKey, locked); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(deviceId), + const DeepCollectionEquality().hash(hasKey), + const DeepCollectionEquality().hash(locked)); @JsonKey(ignore: true) @override @@ -1037,19 +1052,19 @@ class _$_CredentialData extends _CredentialData { final String name; @override final String secret; - @JsonKey(defaultValue: OathType.totp) + @JsonKey() @override final OathType oathType; - @JsonKey(defaultValue: HashAlgorithm.sha1) + @JsonKey() @override final HashAlgorithm hashAlgorithm; - @JsonKey(defaultValue: 6) + @JsonKey() @override final int digits; - @JsonKey(defaultValue: 30) + @JsonKey() @override final int period; - @JsonKey(defaultValue: 0) + @JsonKey() @override final int counter; @@ -1063,21 +1078,28 @@ class _$_CredentialData extends _CredentialData { return identical(this, other) || (other.runtimeType == runtimeType && other is _CredentialData && - (identical(other.issuer, issuer) || other.issuer == issuer) && - (identical(other.name, name) || other.name == name) && - (identical(other.secret, secret) || other.secret == secret) && - (identical(other.oathType, oathType) || - other.oathType == oathType) && - (identical(other.hashAlgorithm, hashAlgorithm) || - other.hashAlgorithm == hashAlgorithm) && - (identical(other.digits, digits) || other.digits == digits) && - (identical(other.period, period) || other.period == period) && - (identical(other.counter, counter) || other.counter == counter)); + const DeepCollectionEquality().equals(other.issuer, issuer) && + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.secret, secret) && + const DeepCollectionEquality().equals(other.oathType, oathType) && + const DeepCollectionEquality() + .equals(other.hashAlgorithm, hashAlgorithm) && + const DeepCollectionEquality().equals(other.digits, digits) && + const DeepCollectionEquality().equals(other.period, period) && + const DeepCollectionEquality().equals(other.counter, counter)); } @override - int get hashCode => Object.hash(runtimeType, issuer, name, secret, oathType, - hashAlgorithm, digits, period, counter); + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(issuer), + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(secret), + const DeepCollectionEquality().hash(oathType), + const DeepCollectionEquality().hash(hashAlgorithm), + const DeepCollectionEquality().hash(digits), + const DeepCollectionEquality().hash(period), + const DeepCollectionEquality().hash(counter)); @JsonKey(ignore: true) @override diff --git a/lib/oath/views/account_list.dart b/lib/oath/views/account_list.dart index 3dcf7ca2..12a171b1 100755 --- a/lib/oath/views/account_list.dart +++ b/lib/oath/views/account_list.dart @@ -5,7 +5,7 @@ import '../models.dart'; import 'account_view.dart'; class AccountList extends StatelessWidget { - final DeviceNode device; + final YubiKeyData device; final List credentials; final List favorites; const AccountList(this.device, this.credentials, this.favorites, {Key? key}) diff --git a/lib/oath/views/account_view.dart b/lib/oath/views/account_view.dart index c5357c90..2476d6f1 100755 --- a/lib/oath/views/account_view.dart +++ b/lib/oath/views/account_view.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../management/models.dart'; import '../../widgets/circle_timer.dart'; import '../../app/models.dart'; import '../models.dart'; @@ -35,7 +36,7 @@ class _ExpireNotifier extends StateNotifier { } class AccountView extends ConsumerWidget { - final DeviceNode device; + final YubiKeyData device; final OathCredential credential; final OathCode? code; const AccountView(this.device, this.credential, this.code, {Key? key}) @@ -117,7 +118,7 @@ class AccountView extends ConsumerWidget { } try { await ref - .read(credentialListProvider(device.path).notifier) + .read(credentialListProvider(device.node.path).notifier) .calculate(credential); } finally { close?.call(); diff --git a/lib/oath/views/oath_screen.dart b/lib/oath/views/oath_screen.dart index 988a19a0..bc67aa6e 100755 --- a/lib/oath/views/oath_screen.dart +++ b/lib/oath/views/oath_screen.dart @@ -6,12 +6,12 @@ import '../state.dart'; import 'account_list.dart'; class OathScreen extends ConsumerWidget { - final DeviceNode device; + final YubiKeyData device; const OathScreen(this.device, {Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(oathStateProvider(device.path)); + final state = ref.watch(oathStateProvider(device.node.path)); if (state == null) { return Column( @@ -35,7 +35,7 @@ class OathScreen extends ConsumerWidget { decoration: const InputDecoration(labelText: 'Password'), onSubmitted: (value) async { final result = await ref - .read(oathStateProvider(device.path).notifier) + .read(oathStateProvider(device.node.path).notifier) .unlock(value); if (!result) { ScaffoldMessenger.of(context).showSnackBar( @@ -51,7 +51,7 @@ class OathScreen extends ConsumerWidget { ), ); } else { - final accounts = ref.watch(credentialListProvider(device.path)); + final accounts = ref.watch(credentialListProvider(device.node.path)); if (accounts == null) { return Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/pubspec.lock b/pubspec.lock index 78900d1e..a2ee7c6e 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -323,7 +323,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.3" meta: dependency: transitive description: @@ -615,7 +615,7 @@ packages: name: window_manager url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8e293880..957cb9b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: flutter_riverpod: ^1.0.0 json_annotation: ^4.3.0 freezed_annotation: ^1.0.0 - window_manager: ^0.1.3 + window_manager: ^0.1.4 dev_dependencies: flutter_test: diff --git a/yubikey-manager b/yubikey-manager index ab71cb6d..765ccf63 160000 --- a/yubikey-manager +++ b/yubikey-manager @@ -1 +1 @@ -Subproject commit ab71cb6da0f19926400837d0b95fdde075830663 +Subproject commit 765ccf63d9ccc972858d71730b9712514a9b0e0d