mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 08:22:16 +03:00
Add OATH reset dialog.
This commit is contained in:
parent
dbf27f7972
commit
276005b868
@ -6,6 +6,7 @@ import '../app/state.dart';
|
||||
import 'state.dart';
|
||||
import 'views/add_account_page.dart';
|
||||
import 'views/password_dialog.dart';
|
||||
import 'views/reset_dialog.dart';
|
||||
|
||||
List<MenuAction> buildOathMenuActions(
|
||||
BuildContext context, AutoDisposeProviderRef ref) {
|
||||
@ -41,14 +42,10 @@ List<MenuAction> buildOathMenuActions(
|
||||
text: 'Factory reset',
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
action: () {
|
||||
ScaffoldMessenger.of(context)
|
||||
..clearSnackBars()
|
||||
..showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Not implemented'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(device),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
|
@ -39,7 +39,7 @@ final oathStateProvider = StateNotifierProvider.autoDispose
|
||||
.family<OathStateNotifier, OathState?, List<String>>(
|
||||
(ref, devicePath) {
|
||||
final session = ref.watch(_sessionProvider(devicePath));
|
||||
final notifier = OathStateNotifier(session, ref.read);
|
||||
final notifier = OathStateNotifier(session, ref);
|
||||
session
|
||||
..setErrorHandler('state-reset', (_) async {
|
||||
ref.refresh(_sessionProvider(devicePath));
|
||||
@ -58,20 +58,20 @@ final oathStateProvider = StateNotifierProvider.autoDispose
|
||||
|
||||
class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
final RpcNodeSession _session;
|
||||
final Reader _read;
|
||||
OathStateNotifier(this._session, this._read) : super(null);
|
||||
final Ref _ref;
|
||||
OathStateNotifier(this._session, this._ref) : super(null);
|
||||
|
||||
refresh() async {
|
||||
var result = await _session.command('get');
|
||||
log.config('application status', jsonEncode(result));
|
||||
var oathState = OathState.fromJson(result['data']);
|
||||
final key = _read(_lockKeyProvider(_session.devicePath));
|
||||
final key = _ref.read(_lockKeyProvider(_session.devicePath));
|
||||
if (oathState.locked && key != null) {
|
||||
final result = await _session.command('validate', params: {'key': key});
|
||||
if (result['unlocked']) {
|
||||
oathState = oathState.copyWith(locked: false);
|
||||
} else {
|
||||
_read(_lockKeyProvider(_session.devicePath).notifier).unsetKey();
|
||||
_ref.read(_lockKeyProvider(_session.devicePath).notifier).unsetKey();
|
||||
}
|
||||
}
|
||||
if (mounted) {
|
||||
@ -79,6 +79,12 @@ class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
await _session.command('reset');
|
||||
_ref.read(_lockKeyProvider(_session.devicePath).notifier).unsetKey();
|
||||
_ref.refresh(_sessionProvider(_session.devicePath));
|
||||
}
|
||||
|
||||
Future<bool> unlock(String password) async {
|
||||
var result =
|
||||
await _session.command('derive', params: {'password': password});
|
||||
@ -86,7 +92,7 @@ class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
final status = await _session.command('validate', params: {'key': key});
|
||||
if (mounted && status['unlocked']) {
|
||||
log.config('applet unlocked');
|
||||
_read(_lockKeyProvider(_session.devicePath).notifier).setKey(key);
|
||||
_ref.read(_lockKeyProvider(_session.devicePath).notifier).setKey(key);
|
||||
state = state?.copyWith(locked: false);
|
||||
}
|
||||
return status['unlocked'];
|
||||
@ -97,8 +103,8 @@ class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
var result =
|
||||
await _session.command('derive', params: {'password': password});
|
||||
log.info(
|
||||
'Check ${_read(_lockKeyProvider(_session.devicePath))} == ${result['key']}');
|
||||
return _read(_lockKeyProvider(_session.devicePath)) == result['key'];
|
||||
'Check ${_ref.read(_lockKeyProvider(_session.devicePath))} == ${result['key']}');
|
||||
return _ref.read(_lockKeyProvider(_session.devicePath)) == result['key'];
|
||||
}
|
||||
|
||||
Future<bool> setPassword(String? current, String password) async {
|
||||
@ -117,7 +123,7 @@ class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
var key = result['key'];
|
||||
await _session.command('set_key', params: {'key': key});
|
||||
log.config('OATH key set');
|
||||
_read(_lockKeyProvider(_session.devicePath).notifier).setKey(key);
|
||||
_ref.read(_lockKeyProvider(_session.devicePath).notifier).setKey(key);
|
||||
if (mounted) {
|
||||
state = state?.copyWith(hasKey: true);
|
||||
}
|
||||
@ -131,7 +137,7 @@ class OathStateNotifier extends StateNotifier<OathState?> {
|
||||
}
|
||||
}
|
||||
await _session.command('unset_key');
|
||||
_read(_lockKeyProvider(_session.devicePath).notifier).unsetKey();
|
||||
_ref.read(_lockKeyProvider(_session.devicePath).notifier).unsetKey();
|
||||
if (mounted) {
|
||||
state = state?.copyWith(hasKey: false, locked: false);
|
||||
}
|
||||
|
@ -140,13 +140,13 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
ElevatedButton(
|
||||
onPressed: _newPassword.isNotEmpty &&
|
||||
_newPassword == _confirmPassword &&
|
||||
(!hasKey || _currentPassword.isNotEmpty)
|
||||
|
56
lib/oath/views/reset_dialog.dart
Executable file
56
lib/oath/views/reset_dialog.dart
Executable file
@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:yubico_authenticator/oath/state.dart';
|
||||
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
|
||||
class ResetDialog extends ConsumerWidget {
|
||||
final DeviceNode device;
|
||||
const ResetDialog(this.device, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// If current device changes, we need to pop back to the main Page.
|
||||
ref.listen<DeviceNode?>(currentDeviceProvider, (previous, next) {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Reset to defaults?'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Warning! This will irrevocably delete all OATH TOTP/HOTP accounts from your YubiKey.'),
|
||||
const Text(''),
|
||||
Text(
|
||||
'You OATH credentials, as well as any password set, will be removed from this YubiKey. Make sure to first disable these from their respective web sites to avoid being locked out of your accounts.',
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await ref.read(oathStateProvider(device.path).notifier).reset();
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('OATH application reset'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('Reset YubiKey'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user