add PlatformException extension

This commit is contained in:
Adam Velebil 2023-01-09 17:22:34 +01:00
parent 1163ebb376
commit fb7ce469ce
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
12 changed files with 170 additions and 34 deletions

View File

@ -26,8 +26,8 @@ import '../../app/logging.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../app/views/user_interaction.dart';
import '../../cancellation_exception.dart';
import '../../core/models.dart';
import '../../exception/platform_exception_decoder.dart';
import '../../oath/models.dart';
import '../../oath/state.dart';
@ -136,11 +136,8 @@ final addCredentialToAnyProvider =
var result = jsonDecode(resultString);
return OathCredential.fromJson(result['credential']);
} on PlatformException catch (pe) {
if (CancellationException.isCancellation(pe)) {
throw CancellationException();
}
_log.error('Failed to add account.', pe);
rethrow;
throw pe.decode();
}
});
@ -216,10 +213,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
_log.debug('Calculate', resultJson);
return OathCode.fromJson(jsonDecode(resultJson));
} on PlatformException catch (pe) {
if (CancellationException.isCancellation(pe)) {
throw CancellationException();
}
rethrow;
throw pe.decode();
} finally {
touchTimer?.cancel();
controller?.close();
@ -236,11 +230,8 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
var result = jsonDecode(resultString);
return OathCredential.fromJson(result['credential']);
} on PlatformException catch (pe) {
if (CancellationException.isCancellation(pe)) {
throw CancellationException();
}
_log.error('Failed to add account.', pe);
rethrow;
throw pe.decode();
}
}
@ -258,10 +249,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
return OathCredential.fromJson(responseJson);
} on PlatformException catch (pe) {
_log.debug('Failed to execute renameOathCredential: ${pe.message}');
if (CancellationException.isCancellation(pe)) {
throw CancellationException();
}
rethrow;
throw pe.decode();
}
}
@ -272,10 +260,7 @@ class _AndroidCredentialListNotifier extends OathCredentialListNotifier {
.invokeMethod('deleteAccount', {'credentialId': credential.id});
} on PlatformException catch (e) {
_log.debug('Received exception: $e');
if (CancellationException.isCancellation(e)) {
throw CancellationException();
}
rethrow;
throw e.decode();
}
}
}

View File

@ -16,7 +16,7 @@
import 'package:flutter/material.dart';
import 'package:yubico_authenticator/app/state.dart';
import 'package:yubico_authenticator/cancellation_exception.dart';
import 'package:yubico_authenticator/exception/cancellation_exception.dart';
import 'package:yubico_authenticator/theme.dart';
import 'qr_scanner_view.dart';

View File

@ -17,7 +17,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../cancellation_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../core/state.dart';
import '../../fido/views/fido_screen.dart';
import '../../oath/models.dart';

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ApduException implements Exception {
final int sw;
final String message;
final String? details;
ApduException(this.sw, this.message, this.details);
}

View File

@ -14,12 +14,6 @@
* limitations under the License.
*/
import 'package:flutter/services.dart';
class CancellationException implements Exception {
CancellationException();
static isCancellation(PlatformException pe) =>
pe.code == 'CancellationException';
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:flutter/services.dart';
import 'apdu_exception.dart';
import 'cancellation_exception.dart';
extension Decoder on PlatformException {
bool _isCancellation() => code == 'CancellationException';
bool _isApduException() => code == 'ApduException';
Exception decode() {
if (_isCancellation()) {
return CancellationException();
}
if (message != null && _isApduException()) {
final regExp = RegExp(
r'^com.yubico.yubikit.core.smartcard.ApduException: APDU error: 0x(.*)$');
final firstMatch = regExp.firstMatch(message!);
if (firstMatch != null) {
final hexSw = firstMatch.group(1);
final sw = int.tryParse(hexSw!, radix: 16);
if (sw != null) {
return ApduException(sw, 'SW: 0x$hexSw', details);
}
}
}
// original exception
return this;
}
}

View File

@ -25,7 +25,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../cancellation_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/circle_timer.dart';
import '../../widgets/custom_icons.dart';
import '../models.dart';

View File

@ -20,7 +20,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/shortcuts.dart';
import '../../app/state.dart';
import '../../cancellation_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/menu_list_tile.dart';
import '../models.dart';
import '../state.dart';

View File

@ -31,7 +31,8 @@ import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../app/views/user_interaction.dart';
import '../../cancellation_exception.dart';
import '../../exception/apdu_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../core/state.dart';
import '../../desktop/models.dart';
import '../../management/models.dart';
@ -195,6 +196,8 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
// TODO: Make this cleaner than importing desktop specific RpcError.
if (e is RpcError) {
errorMessage = e.message;
} else if (e is ApduException) {
errorMessage = e.message;
} else {
errorMessage = e.toString();
}

View File

@ -17,11 +17,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:yubico_authenticator/cancellation_exception.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../app/state.dart';
import '../../exception/cancellation_exception.dart';
import '../../widgets/responsive_dialog.dart';
import '../models.dart';
import '../state.dart';

View File

@ -22,7 +22,7 @@ import 'package:logging/logging.dart';
import '../../app/logging.dart';
import '../../app/message.dart';
import '../../app/models.dart';
import '../../cancellation_exception.dart';
import '../../exception/cancellation_exception.dart';
import '../../desktop/models.dart';
import '../../widgets/responsive_dialog.dart';
import '../../widgets/utf8_utils.dart';

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:yubico_authenticator/exception/apdu_exception.dart';
import 'package:yubico_authenticator/exception/cancellation_exception.dart';
import 'package:yubico_authenticator/exception/platform_exception_decoder.dart';
PlatformException mockApdu(String message) =>
PlatformException(code: 'ApduException', message: message);
void main() {
test('Recognize cancellation exception', () {
final pe = PlatformException(
code: 'CancellationException',
message: null,
details: null,
stacktrace: null);
expect(pe.decode(), isA<CancellationException>());
});
test('Recognize apdu exception', () {
var pe = mockApdu(
'com.yubico.yubikit.core.smartcard.ApduException: APDU error: 0x6f00');
expect(
pe.decode(),
const TypeMatcher<ApduException>()
.having((ae) => ae.sw, 'SW', 28416)
.having((ae) => ae.message, 'message', 'SW: 0x6f00'));
pe = mockApdu(
'com.yubico.yubikit.core.smartcard.ApduException: APDU error: 0xIJKLMNO');
expect(pe.decode(), isNot(const TypeMatcher<ApduException>()));
pe = mockApdu(
'com.yubico.yubikit.core.smartcard.ApduException: APDU error: 6f00');
expect(pe.decode(), isNot(const TypeMatcher<ApduException>()));
pe = mockApdu(
'com.yubico.yubikit.core.smartcard.ApduException: APDU error:');
expect(pe.decode(), isNot(const TypeMatcher<ApduException>()));
pe = mockApdu('');
expect(pe.decode(), isNot(const TypeMatcher<ApduException>()));
});
test('Rethrow', () {
var pe = PlatformException(
code: 'some code',
message: 'some message',
details: 'some details',
stacktrace: 'and stacktrace');
expect(
pe.decode(),
const TypeMatcher<PlatformException>()
.having((pe) => pe.code, 'code', 'some code')
.having((pe) => pe.message, 'message', 'some message')
.having((pe) => pe.details, 'details', 'some details')
.having((pe) => pe.stacktrace, 'stacktrace', 'and stacktrace'));
});
}