Improved error handling, added multithreading for fromJson calls, made receive sharing intent more robust

This commit is contained in:
Anton Stubenbord
2022-11-13 14:41:42 +01:00
parent afbd4bddb4
commit 1cafd5d246
43 changed files with 644 additions and 746 deletions

View File

@@ -1,7 +1,7 @@
import 'dart:developer';
import 'dart:io';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:paperless_mobile/core/bloc/global_error_cubit.dart';
import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/core/store/local_vault.dart';
import 'package:paperless_mobile/di_initializer.dart';
@@ -17,13 +17,11 @@ const authenticationKey = "authentication";
@singleton
class AuthenticationCubit extends Cubit<AuthenticationState> {
final LocalVault localStore;
final GlobalErrorCubit errorCubit;
final AuthenticationService authenticationService;
AuthenticationCubit(
this.localStore,
this.authenticationService,
this.errorCubit,
) : super(AuthenticationState.initial);
Future<void> initialize() {
@@ -34,7 +32,6 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
required UserCredentials credentials,
required String serverUrl,
ClientCertificate? clientCertificate,
bool propagateEventOnError = true,
}) async {
assert(credentials.username != null && credentials.password != null);
try {
@@ -75,60 +72,37 @@ class AuthenticationCubit extends Cubit<AuthenticationState> {
} on TlsException catch (_) {
const error =
ErrorMessage(ErrorCode.invalidClientCertificateConfiguration);
if (propagateEventOnError) {
errorCubit.add(error);
}
throw error;
} on SocketException catch (err) {
late ErrorMessage error;
if (err.message.contains("connection timed out")) {
error = const ErrorMessage(ErrorCode.requestTimedOut);
throw const ErrorMessage(ErrorCode.requestTimedOut);
} else {
error = ErrorMessage.unknown();
throw ErrorMessage.unknown();
}
if (propagateEventOnError) {
errorCubit.add(error);
}
rethrow;
} on ErrorMessage catch (error) {
if (propagateEventOnError) {
errorCubit.add(error);
}
rethrow;
}
}
Future<void> restoreSessionState({
bool propagateEventOnError = true,
}) async {
try {
final storedAuth = await localStore.loadAuthenticationInformation();
final appSettings = await localStore.loadApplicationSettings() ??
ApplicationSettingsState.defaultSettings;
Future<void> restoreSessionState() async {
final storedAuth = await localStore.loadAuthenticationInformation();
final appSettings = await localStore.loadApplicationSettings() ??
ApplicationSettingsState.defaultSettings;
if (storedAuth == null || !storedAuth.isValid) {
if (storedAuth == null || !storedAuth.isValid) {
emit(AuthenticationState(isAuthenticated: false, wasLoginStored: false));
} else {
if (!appSettings.isLocalAuthenticationEnabled ||
await authenticationService
.authenticateLocalUser("Authenticate to log back in")) {
registerSecurityContext(storedAuth.clientCertificate);
emit(
AuthenticationState(isAuthenticated: false, wasLoginStored: false));
AuthenticationState(
isAuthenticated: true,
wasLoginStored: true,
authentication: storedAuth,
),
);
} else {
if (!appSettings.isLocalAuthenticationEnabled ||
await authenticationService
.authenticateLocalUser("Authenticate to log back in")) {
registerSecurityContext(storedAuth.clientCertificate);
emit(
AuthenticationState(
isAuthenticated: true,
wasLoginStored: true,
authentication: storedAuth,
),
);
} else {
emit(AuthenticationState(
isAuthenticated: false, wasLoginStored: true));
}
}
} on ErrorMessage catch (error) {
if (propagateEventOnError) {
errorCubit.add(error);
emit(AuthenticationState(isAuthenticated: false, wasLoginStored: true));
}
}
}

View File

@@ -26,10 +26,19 @@ class AuthenticationService {
required String password,
required String serverUrl,
}) async {
final response = await httpClient.post(
Uri.parse("/api/token/"),
body: {"username": username, "password": password},
);
late Response response;
try {
response = await httpClient.post(
Uri.parse("/api/token/"),
body: {"username": username, "password": password},
);
} on FormatException catch (e) {
final source = e.source;
if (source is String &&
source.contains("400 No required SSL certificate was sent")) {
throw const ErrorMessage(ErrorCode.missingClientCertificate);
}
}
if (response.statusCode == 200) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
return data['token'];

View File

@@ -1,12 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_mobile/core/model/error_message.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/view/widgets/client_certificate_form_field.dart';
import 'package:paperless_mobile/features/login/view/widgets/server_address_form_field.dart';
import 'package:paperless_mobile/features/login/view/widgets/user_credentials_form_field.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/util.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
@@ -72,7 +74,8 @@ class _LoginPageState extends State<LoginPage> {
return ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(
Theme.of(context).colorScheme.primaryContainer),
Theme.of(context).colorScheme.primaryContainer,
),
elevation: const MaterialStatePropertyAll(0),
),
onPressed: _login,
@@ -82,19 +85,25 @@ class _LoginPageState extends State<LoginPage> {
);
}
void _login() {
void _login() async {
FocusScope.of(context).unfocus();
if (_formKey.currentState?.saveAndValidate() ?? false) {
setState(() => _isLoginLoading = true);
final form = _formKey.currentState?.value;
BlocProvider.of<AuthenticationCubit>(context)
.login(
credentials: form?[UserCredentialsFormField.fkCredentials],
serverUrl: form?[ServerAddressFormField.fkServerAddress],
clientCertificate:
form?[ClientCertificateFormField.fkClientCertificate],
)
.whenComplete(() => setState(() => _isLoginLoading = false));
final form = _formKey.currentState!.value;
try {
await BlocProvider.of<AuthenticationCubit>(context).login(
credentials: form[UserCredentialsFormField.fkCredentials],
serverUrl: form[ServerAddressFormField.fkServerAddress],
clientCertificate:
form[ClientCertificateFormField.fkClientCertificate],
);
} on ErrorMessage catch (error) {
showError(context, error);
} catch (unknownError) {
showSnackBar(context, unknownError.toString());
} finally {
setState(() => _isLoginLoading = false);
}
}
}
}