Fixed login and error handling

This commit is contained in:
Anton Stubenbord
2022-12-30 01:28:43 +01:00
parent bf0e186646
commit 2326c9d1d6
43 changed files with 502 additions and 447 deletions

View File

@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:math';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
@@ -10,9 +11,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:http_interceptor/http/intercepted_client.dart';
import 'package:hive/hive.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl_standalone.dart';
@@ -22,7 +21,9 @@ import 'package:paperless_mobile/core/bloc/bloc_changes_observer.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
import 'package:paperless_mobile/core/global/constants.dart';
import 'package:paperless_mobile/core/interceptor/authentication.interceptor.dart';
import 'package:paperless_mobile/core/interceptor/dio_http_error_interceptor.dart';
import 'package:paperless_mobile/core/interceptor/language_header.interceptor.dart';
import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_interceptor.dart';
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
import 'package:paperless_mobile/core/repository/impl/correspondent_repository_impl.dart';
import 'package:paperless_mobile/core/repository/impl/document_type_repository_impl.dart';
@@ -33,17 +34,16 @@ import 'package:paperless_mobile/core/repository/label_repository.dart';
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
import 'package:paperless_mobile/core/security/authentication_aware_dio_manager.dart';
import 'package:paperless_mobile/core/service/connectivity_status.service.dart';
import 'package:paperless_mobile/core/service/dio_file_service.dart';
import 'package:paperless_mobile/core/service/file_service.dart';
import 'package:paperless_mobile/core/store/local_vault.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/extensions/security_context_extension.dart';
import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.dart';
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
import 'package:paperless_mobile/features/home/view/home_page.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/features/login/view/login_page.dart';
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
@@ -53,24 +53,48 @@ import 'package:paperless_mobile/util.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
void main() async {
Bloc.observer = BlocChangesObserver();
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: await getApplicationDocumentsDirectory(),
);
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await findSystemLocale();
// Required for self signed client certificates
final dioWrapper = AuthenticationAwareDioManager();
// Initialize External dependencies
final connectivity = Connectivity();
final encryptedSharedPreferences = EncryptedSharedPreferences();
final localAuthentication = LocalAuthentication();
// Initialize other utility classes
final connectivityStatusService = ConnectivityStatusServiceImpl(connectivity);
final localVault = LocalVaultImpl(encryptedSharedPreferences);
final localAuthService =
LocalAuthenticationService(localVault, localAuthentication);
final hiveDir = await getApplicationDocumentsDirectory();
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: hiveDir,
);
final appSettingsCubit = ApplicationSettingsCubit(localAuthService);
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
final languageHeaderInterceptor = LanguageHeaderInterceptor(
appSettingsCubit.state.preferredLocaleSubtag,
);
// Required for self signed client certificates
final dioWrapper = AuthenticationAwareDioManager([
DioHttpErrorInterceptor(),
PrettyDioLogger(
compact: true,
responseBody: false,
responseHeader: false,
request: false,
requestBody: false,
requestHeader: false,
),
languageHeaderInterceptor,
]);
// Initialize Paperless APIs
final authApi = PaperlessAuthenticationApiImpl(dioWrapper.client);
@@ -79,14 +103,6 @@ void main() async {
final statsApi = PaperlessServerStatsApiImpl(dioWrapper.client);
final savedViewsApi = PaperlessSavedViewsApiImpl(dioWrapper.client);
// Initialize other utility classes
final connectivityStatusService = ConnectivityStatusServiceImpl(connectivity);
final localVault = LocalVaultImpl(encryptedSharedPreferences);
final localAuthService =
LocalAuthenticationService(localVault, localAuthentication);
// Initialize Repositories
// Initialize Blocs/Cubits
final connectivityCubit = ConnectivityCubit(connectivityStatusService);
// Remove temporarily downloaded files.
@@ -95,15 +111,21 @@ void main() async {
// Load application settings and stored authentication data
await connectivityCubit.initialize();
// Create repositories
final tagRepository = TagRepositoryImpl(labelsApi);
final correspondentRepository = CorrespondentRepositoryImpl(labelsApi);
final documentTypeRepository = DocumentTypeRepositoryImpl(labelsApi);
final storagePathRepository = StoragePathRepositoryImpl(labelsApi);
final savedViewRepository = SavedViewRepositoryImpl(savedViewsApi);
//Create cubits/blocs
final authCubit = AuthenticationCubit(
localVault,
localAuthService,
authApi,
dioWrapper,
);
String? currentServerUrl;
String? currentAuthToken;
await authCubit
.restoreSessionState(appSettingsCubit.state.isLocalAuthenticationEnabled);
if (authCubit.state.isAuthenticated) {
final auth = authCubit.state.authentication!;
@@ -112,31 +134,11 @@ void main() async {
authToken: auth.token,
clientCertificate: auth.clientCertificate,
);
currentServerUrl = auth.serverUrl;
currentAuthToken = auth.token;
}
SecurityContext securityContext = SecurityContext();
authCubit.stream.asBroadcastStream().listen((event) {
if (event.isAuthenticated) {
final auth = event.authentication!;
securityContext =
SecurityContext().withClientCertificate(auth.clientCertificate);
currentServerUrl = auth.serverUrl;
currentAuthToken = auth.token;
} else {
securityContext = SecurityContext();
currentServerUrl = null;
currentAuthToken = null;
}
});
// Create repositories
final tagRepository = TagRepositoryImpl(labelsApi);
final correspondentRepository = CorrespondentRepositoryImpl(labelsApi);
final documentTypeRepository = DocumentTypeRepositoryImpl(labelsApi);
final storagePathRepository = StoragePathRepositoryImpl(labelsApi);
final savedViewRepository = SavedViewRepositoryImpl(savedViewsApi);
//Update language header in interceptor on language change.
appSettingsCubit.stream.listen((event) => languageHeaderInterceptor
.preferredLocaleSubtag = event.preferredLocaleSubtag);
runApp(
MultiProvider(
@@ -146,44 +148,11 @@ void main() async {
Provider<PaperlessLabelsApi>.value(value: labelsApi),
Provider<PaperlessServerStatsApi>.value(value: statsApi),
Provider<PaperlessSavedViewsApi>.value(value: savedViewsApi),
ProxyProvider<SecurityContext, cm.CacheManager>(
Provider<cm.CacheManager>(
create: (context) => cm.CacheManager(
cm.Config(
'cacheKey',
fileService: cm.HttpFileService(
httpClient: InterceptedClient.build(
interceptors: [
AuthenticationInterceptor(
serverUrl: currentServerUrl,
token: currentAuthToken,
),
],
client: IOClient(
HttpClient(
context: securityContext,
),
),
),
),
),
),
update: (context, securityContext, previous) => cm.CacheManager(
cm.Config(
'cacheKey',
fileService: cm.HttpFileService(
httpClient: InterceptedClient.build(
interceptors: [
AuthenticationInterceptor(
serverUrl: currentServerUrl,
token: currentAuthToken,
),
],
client: IOClient(
HttpClient(
context: securityContext,
),
),
)),
fileService: DioFileService(dioWrapper.client),
),
),
),
@@ -214,6 +183,8 @@ void main() async {
providers: [
BlocProvider<AuthenticationCubit>.value(value: authCubit),
BlocProvider<ConnectivityCubit>.value(value: connectivityCubit),
BlocProvider<ApplicationSettingsCubit>.value(
value: appSettingsCubit),
],
child: const PaperlessMobileEntrypoint(),
),
@@ -282,9 +253,6 @@ class _PaperlessMobileEntrypointState extends State<PaperlessMobileEntrypoint> {
BlocProvider(
create: (context) => PaperlessServerInformationCubit(context.read()),
),
BlocProvider(
create: (context) => ApplicationSettingsCubit(),
),
],
child: BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
builder: (context, settings) {
@@ -423,7 +391,8 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
}
},
builder: (context, authentication) {
if (authentication.isAuthenticated) {
if (authentication.isAuthenticated &&
(authentication.wasLocalAuthenticationSuccessful ?? true)) {
return const HomePage();
} else {
if (authentication.wasLoginStored &&
@@ -461,12 +430,20 @@ class BiometricAuthenticationPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () => context.read<AuthenticationCubit>().logout(),
onPressed: () {
context.read<AuthenticationCubit>().logout();
context.read();
HydratedBloc.storage.clear();
},
child: const Text("Log out"),
),
ElevatedButton(
onPressed: () =>
context.read<AuthenticationCubit>().restoreSessionState(),
onPressed: () => context
.read<AuthenticationCubit>()
.restoreSessionState(context
.read<ApplicationSettingsCubit>()
.state
.isLocalAuthenticationEnabled),
child: const Text("Authenticate"),
),
],