nc-photos/app/lib/protected_page_handler.dart

159 lines
4.3 KiB
Dart
Raw Normal View History

2024-05-25 09:51:42 +02:00
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:local_auth/local_auth.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/controller/pref_controller.dart';
import 'package:nc_photos/widget/protected_page_password_auth_dialog.dart';
import 'package:nc_photos/widget/protected_page_pin_auth_dialog.dart';
import 'package:np_codegen/np_codegen.dart';
import 'package:np_string/np_string.dart';
part 'protected_page_handler.g.dart';
enum ProtectedPageAuthType {
biometric,
pin,
password,
;
}
class ProtectedPageAuthException implements Exception {
const ProtectedPageAuthException([this.message]);
@override
String toString() => "ProtectedPageAuthException: $message";
final dynamic message;
}
extension ProtectedPageBuildContextExtension on NavigatorState {
Future<T?>
pushReplacementNamedProtected<T extends Object?, U extends Object?>(
2024-05-25 09:51:42 +02:00
String routeName, {
U? result,
Object? arguments,
}) async {
if (await _auth()) {
return pushReplacementNamed(routeName,
arguments: arguments, result: result);
} else {
throw const ProtectedPageAuthException();
}
}
Future<T?> pushNamedProtected<T extends Object?>(
2024-05-25 09:51:42 +02:00
String routeName, {
Object? arguments,
}) async {
if (await _auth()) {
return pushNamed(routeName, arguments: arguments);
} else {
throw const ProtectedPageAuthException();
}
}
Future<T?> pushProtected<T extends Object?>(Route<T> route) async {
if (await _auth()) {
return push(route);
} else {
throw const ProtectedPageAuthException();
}
}
2024-05-25 09:51:42 +02:00
Future<bool> _auth() async {
final securePrefController = context.read<SecurePrefController>();
switch (securePrefController.protectedPageAuthTypeValue) {
case null:
// unprotected
return true;
case ProtectedPageAuthType.biometric:
return _authBiometric(securePrefController);
case ProtectedPageAuthType.pin:
return _authPin(securePrefController);
case ProtectedPageAuthType.password:
return _authPassword(securePrefController);
}
}
Future<bool> _authBiometric(SecurePrefController securePrefController) async {
if (await _BiometricAuthHandler().auth()) {
return true;
} else {
if (securePrefController.protectedPageAuthPasswordValue != null) {
return _authPassword(securePrefController);
} else {
return _authPin(securePrefController);
}
}
}
Future<bool> _authPin(SecurePrefController securePrefController) =>
_PinAuthHandler(context, securePrefController.protectedPageAuthPinValue!)
.auth();
Future<bool> _authPassword(SecurePrefController securePrefController) =>
_PasswordAuthHandler(
context, securePrefController.protectedPageAuthPasswordValue!)
.auth();
}
abstract class _AuthHandler {
Future<bool> auth();
}
@npLog
class _BiometricAuthHandler implements _AuthHandler {
@override
Future<bool> auth() async {
try {
final localAuth = LocalAuthentication();
final available = await localAuth.getAvailableBiometrics();
if (available.isEmpty) {
return false;
}
return await localAuth.authenticate(
localizedReason: L10n.global().appLockUnlockHint,
options: const AuthenticationOptions(
biometricOnly: true,
),
);
} catch (e, stackTrace) {
_log.severe("[auth] Exception", e, stackTrace);
return false;
}
}
}
class _PinAuthHandler implements _AuthHandler {
const _PinAuthHandler(this.context, this.pin);
@override
Future<bool> auth() async {
final result = await showDialog<bool>(
context: context,
builder: (context) => ProtectedPagePinAuthDialog(pin: pin),
);
return result == true;
}
final BuildContext context;
final CiString pin;
}
class _PasswordAuthHandler implements _AuthHandler {
const _PasswordAuthHandler(this.context, this.password);
@override
Future<bool> auth() async {
final result = await showDialog<bool>(
context: context,
builder: (context) => ProtectedPagePasswordAuthDialog(password: password),
);
return result == true;
}
final BuildContext context;
final CiString password;
}