From d2553b0d0827e2c58f412ab6f82e16c14d23f73a Mon Sep 17 00:00:00 2001 From: inexcode Date: Wed, 18 May 2022 13:39:11 +0300 Subject: [PATCH] Add auth functions to server_installation_repository.dart --- lib/logic/api_maps/server.dart | 19 +- .../server_installation_repository.dart | 175 +++++++++++++++++- .../server_installation_state.dart | 25 ++- lib/logic/models/hive/server_details.g.dart | 2 +- lib/ui/pages/providers/providers.dart | 6 +- lib/ui/pages/server_details/chart.dart | 2 +- lib/ui/pages/server_details/header.dart | 2 +- ...etails.dart => server_details_screen.dart} | 19 +- .../pages/server_details/server_settings.dart | 2 +- lib/ui/pages/server_details/text_details.dart | 2 +- .../server_details/time_zone/time_zone.dart | 2 +- pubspec.lock | 44 ++++- pubspec.yaml | 2 + 13 files changed, 269 insertions(+), 33 deletions(-) rename lib/ui/pages/server_details/{server_details.dart => server_details_screen.dart} (94%) diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 0cafbf6b..21bceee9 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -5,14 +5,14 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/json/backup.dart'; -import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart'; +import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; import 'api_map.dart'; @@ -34,9 +34,13 @@ class ServerApi extends ApiMap { bool hasLogger; bool isWithToken; String? overrideDomain; + String? customToken; ServerApi( - {this.hasLogger = false, this.isWithToken = true, this.overrideDomain}); + {this.hasLogger = false, + this.isWithToken = true, + this.overrideDomain, + this.customToken}); BaseOptions get options { var options = BaseOptions(); @@ -52,7 +56,12 @@ class ServerApi extends ApiMap { } if (overrideDomain != null) { - options = BaseOptions(baseUrl: 'https://api.$overrideDomain'); + options = BaseOptions( + baseUrl: 'https://api.$overrideDomain', + headers: customToken != null + ? {'Authorization': 'Bearer $customToken'} + : null, + ); } return options; diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index f2ef84f1..6af170f6 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -1,7 +1,12 @@ +import 'dart:io'; + import 'package:basic_utils/basic_utils.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; @@ -11,12 +16,25 @@ import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/json/device_token.dart'; import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart'; import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import '../server_installation/server_installation_cubit.dart'; +class IpNotFoundException implements Exception { + final String message; + + IpNotFoundException(this.message); +} + +class ServerAuthorizationException implements Exception { + final String message; + + ServerAuthorizationException(this.message); +} + class ServerInstallationRepository { Box box = Hive.box(BNames.serverInstallationBox); @@ -43,7 +61,7 @@ class ServerInstallationRepository { ); } - if (getIt().serverDomain?.provider == DnsProvider.Unknown) { + if (serverDomain != null && serverDomain.provider == DnsProvider.Unknown) { return ServerInstallationRecovery( hetznerKey: hetznerToken, cloudFlareKey: cloudflareToken, @@ -52,7 +70,8 @@ class ServerInstallationRepository { serverDetails: serverDetails, rootUser: box.get(BNames.rootUser), currentStep: _getCurrentRecoveryStep( - hetznerToken, cloudflareToken, serverDomain!, serverDetails), + hetznerToken, cloudflareToken, serverDomain, serverDetails), + recoveryCapabilities: await getRecoveryCapabilities(serverDomain), ); } @@ -296,6 +315,158 @@ class ServerInstallationRepository { return await hetznerApi.powerOn(); } + Future getRecoveryCapabilities( + ServerDomain serverDomain, + ) async { + var serverApi = ServerApi( + isWithToken: false, + overrideDomain: serverDomain.domainName, + ); + final serverApiVersion = await serverApi.getApiVersion(); + if (serverApiVersion == null) { + return ServerRecoveryCapabilities.none; + } + try { + final parsedVersion = Version.parse(serverApiVersion); + if (parsedVersion.major == 1 && parsedVersion.minor < 2) { + return ServerRecoveryCapabilities.legacy; + } + return ServerRecoveryCapabilities.loginTokens; + } on FormatException { + return ServerRecoveryCapabilities.none; + } + } + + Future getServerIpFromDomain(ServerDomain serverDomain) async { + final lookup = await DnsUtils.lookupRecord( + serverDomain.domainName, RRecordType.A, + provider: DnsApiProvider.CLOUDFLARE); + if (lookup == null || lookup.isEmpty) { + throw IpNotFoundException('No IP found for domain $serverDomain'); + } + return lookup[0].data; + } + + Future getDeviceName() async { + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (kIsWeb) { + return await deviceInfo.webBrowserInfo + .then((value) => '${value.browserName} ${value.platform}'); + } else { + if (Platform.isAndroid) { + return await deviceInfo.androidInfo + .then((value) => '${value.model} ${value.version.release}'); + } else if (Platform.isIOS) { + return await deviceInfo.iosInfo.then((value) => + '${value.utsname.machine} ${value.systemName} ${value.systemVersion}'); + } else if (Platform.isLinux) { + return await deviceInfo.linuxInfo.then((value) => value.prettyName); + } else if (Platform.isMacOS) { + return await deviceInfo.macOsInfo + .then((value) => '${value.hostName} ${value.computerName}'); + } else if (Platform.isWindows) { + return await deviceInfo.windowsInfo.then((value) => value.computerName); + } + } + return 'Unidentified'; + } + + Future authorizeByLoginToken( + ServerDomain serverDomain, + String loginToken, + ) async { + var serverApi = ServerApi( + isWithToken: false, + overrideDomain: serverDomain.domainName, + ); + final serverIp = await getServerIpFromDomain(serverDomain); + final apiResponse = await serverApi.authorizeDevice( + DeviceToken(device: await getDeviceName(), token: loginToken)); + + if (apiResponse.isSuccess) { + return ServerHostingDetails( + apiToken: apiResponse.data, + volume: ServerVolume( + id: 0, + name: '', + ), + provider: ServerProvider.Unknown, + id: 0, + ip4: serverIp, + startTime: null, + createTime: null, + ); + } + + throw ServerAuthorizationException( + apiResponse.errorMessage ?? apiResponse.data, + ); + } + + Future authorizeByRecoveryToken( + ServerDomain serverDomain, + String recoveryToken, + ) async { + var serverApi = ServerApi( + isWithToken: false, + overrideDomain: serverDomain.domainName, + ); + final apiResponse = await serverApi.useRecoveryToken( + DeviceToken(device: await getDeviceName(), token: recoveryToken)); + + if (apiResponse.isSuccess) { + return ServerHostingDetails( + apiToken: apiResponse.data, + volume: ServerVolume( + id: 0, + name: '', + ), + provider: ServerProvider.Unknown, + id: 0, + ip4: '', + startTime: null, + createTime: null, + ); + } + + throw ServerAuthorizationException( + apiResponse.errorMessage ?? apiResponse.data, + ); + } + + Future authorizeByApiToken( + ServerDomain serverDomain, + String apiToken, + ) async { + var serverApi = ServerApi( + isWithToken: false, + overrideDomain: serverDomain.domainName, + customToken: apiToken, + ); + final deviceAuthKey = await serverApi.createDeviceToken(); + final apiResponse = await serverApi.authorizeDevice( + DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data)); + + if (apiResponse.isSuccess) { + return ServerHostingDetails( + apiToken: apiResponse.data, + volume: ServerVolume( + id: 0, + name: '', + ), + provider: ServerProvider.Unknown, + id: 0, + ip4: '', + startTime: null, + createTime: null, + ); + } + + throw ServerAuthorizationException( + apiResponse.errorMessage ?? apiResponse.data, + ); + } + Future saveServerDetails(ServerHostingDetails serverDetails) async { await getIt().storeServerDetails(serverDetails); } diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index e0fbd1bf..b75b19d6 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -265,8 +265,15 @@ enum RecoveryStep { BackblazeToken, } +enum ServerRecoveryCapabilities { + none, + legacy, + loginTokens, +} + class ServerInstallationRecovery extends ServerInstallationState { final RecoveryStep currentStep; + final ServerRecoveryCapabilities recoveryCapabilities; const ServerInstallationRecovery({ String? hetznerKey, @@ -276,6 +283,7 @@ class ServerInstallationRecovery extends ServerInstallationState { User? rootUser, ServerHostingDetails? serverDetails, required RecoveryStep this.currentStep, + required ServerRecoveryCapabilities this.recoveryCapabilities, }) : super( hetznerKey: hetznerKey, cloudFlareKey: cloudFlareKey, @@ -309,13 +317,16 @@ class ServerInstallationRecovery extends ServerInstallationState { User? rootUser, ServerHostingDetails? serverDetails, RecoveryStep? currentStep, + ServerRecoveryCapabilities? recoveryCapabilities, }) => ServerInstallationRecovery( - hetznerKey: hetznerKey ?? this.hetznerKey, - cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, - backblazeCredential: backblazeCredential ?? this.backblazeCredential, - serverDomain: serverDomain ?? this.serverDomain, - rootUser: rootUser ?? this.rootUser, - serverDetails: serverDetails ?? this.serverDetails, - currentStep: currentStep ?? this.currentStep); + hetznerKey: hetznerKey ?? this.hetznerKey, + cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + backblazeCredential: backblazeCredential ?? this.backblazeCredential, + serverDomain: serverDomain ?? this.serverDomain, + rootUser: rootUser ?? this.rootUser, + serverDetails: serverDetails ?? this.serverDetails, + currentStep: currentStep ?? this.currentStep, + recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities, + ); } diff --git a/lib/logic/models/hive/server_details.g.dart b/lib/logic/models/hive/server_details.g.dart index f52e6b37..3d5ff9c1 100644 --- a/lib/logic/models/hive/server_details.g.dart +++ b/lib/logic/models/hive/server_details.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'server_details.dart'; +part of 'server_details_screen.dart'; // ************************************************************************** // TypeAdapterGenerator diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 108c85e2..333ee66b 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -1,10 +1,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/provider.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; @@ -15,7 +15,7 @@ import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart'; import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart'; -import 'package:selfprivacy/ui/pages/server_details/server_details.dart'; +import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; var navigatorKey = GlobalKey(); @@ -113,7 +113,7 @@ class _Card extends StatelessWidget { context: context, builder: (context) => BrandBottomSheet( isExpended: true, - child: ServerDetails(), + child: ServerDetailsScreen(), ), ); diff --git a/lib/ui/pages/server_details/chart.dart b/lib/ui/pages/server_details/chart.dart index 6ccd86fc..242df725 100644 --- a/lib/ui/pages/server_details/chart.dart +++ b/lib/ui/pages/server_details/chart.dart @@ -1,4 +1,4 @@ -part of 'server_details.dart'; +part of 'server_details_screen.dart'; class _Chart extends StatelessWidget { const _Chart({Key? key}) : super(key: key); diff --git a/lib/ui/pages/server_details/header.dart b/lib/ui/pages/server_details/header.dart index d03d3d08..92abf3f8 100644 --- a/lib/ui/pages/server_details/header.dart +++ b/lib/ui/pages/server_details/header.dart @@ -1,4 +1,4 @@ -part of 'server_details.dart'; +part of 'server_details_screen.dart'; class _Header extends StatelessWidget { const _Header({ diff --git a/lib/ui/pages/server_details/server_details.dart b/lib/ui/pages/server_details/server_details_screen.dart similarity index 94% rename from lib/ui/pages/server_details/server_details.dart rename to lib/ui/pages/server_details/server_details_screen.dart index 90d4144e..56e7e18d 100644 --- a/lib/ui/pages/server_details/server_details.dart +++ b/lib/ui/pages/server_details/server_details_screen.dart @@ -1,11 +1,12 @@ import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; @@ -14,32 +15,32 @@ import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; import 'package:selfprivacy/ui/components/brand_radio_tile/brand_radio_tile.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.dart'; +import 'package:selfprivacy/utils/extensions/duration.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:timezone/timezone.dart'; + import 'cpu_chart.dart'; import 'network_charts.dart'; -import 'package:selfprivacy/utils/extensions/duration.dart'; -part 'server_settings.dart'; -part 'text_details.dart'; part 'chart.dart'; part 'header.dart'; +part 'server_settings.dart'; +part 'text_details.dart'; part 'time_zone/time_zone.dart'; var navigatorKey = GlobalKey(); -class ServerDetails extends StatefulWidget { - const ServerDetails({Key? key}) : super(key: key); +class ServerDetailsScreen extends StatefulWidget { + const ServerDetailsScreen({Key? key}) : super(key: key); @override - _ServerDetailsState createState() => _ServerDetailsState(); + _ServerDetailsScreenState createState() => _ServerDetailsScreenState(); } -class _ServerDetailsState extends State +class _ServerDetailsScreenState extends State with SingleTickerProviderStateMixin { late TabController tabController; diff --git a/lib/ui/pages/server_details/server_settings.dart b/lib/ui/pages/server_details/server_settings.dart index c4d6ed02..cca5fcea 100644 --- a/lib/ui/pages/server_details/server_settings.dart +++ b/lib/ui/pages/server_details/server_settings.dart @@ -1,4 +1,4 @@ -part of 'server_details.dart'; +part of 'server_details_screen.dart'; class _ServerSettings extends StatelessWidget { const _ServerSettings({ diff --git a/lib/ui/pages/server_details/text_details.dart b/lib/ui/pages/server_details/text_details.dart index a4620f0e..3d1c751d 100644 --- a/lib/ui/pages/server_details/text_details.dart +++ b/lib/ui/pages/server_details/text_details.dart @@ -1,4 +1,4 @@ -part of 'server_details.dart'; +part of 'server_details_screen.dart'; class _TextDetails extends StatelessWidget { const _TextDetails({Key? key}) : super(key: key); diff --git a/lib/ui/pages/server_details/time_zone/time_zone.dart b/lib/ui/pages/server_details/time_zone/time_zone.dart index 9802bbb8..cdc0fa65 100644 --- a/lib/ui/pages/server_details/time_zone/time_zone.dart +++ b/lib/ui/pages/server_details/time_zone/time_zone.dart @@ -1,4 +1,4 @@ -part of '../server_details.dart'; +part of '../server_details_screen.dart'; final List locations = timeZoneDatabase.locations.values.toList() ..sort((l1, l2) => diff --git a/pubspec.lock b/pubspec.lock index b7e22bfd..965eb666 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -218,6 +218,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.3" + device_info_plus_linux: + dependency: transitive + description: + name: device_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + device_info_plus_macos: + dependency: transitive + description: + name: device_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0+1" + device_info_plus_web: + dependency: transitive + description: + name: device_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + device_info_plus_windows: + dependency: transitive + description: + name: device_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" dio: dependency: "direct main" description: @@ -750,7 +792,7 @@ packages: source: hosted version: "6.0.2" pub_semver: - dependency: transitive + dependency: "direct main" description: name: pub_semver url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index 1a360bbe..f8b02e64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: crypt: ^4.2.1 cubit_form: ^2.0.1 cupertino_icons: ^1.0.4 + device_info_plus: ^3.2.3 dio: ^4.0.4 dynamic_color: ^1.2.2 easy_localization: ^3.0.0 @@ -36,6 +37,7 @@ dependencies: package_info: ^2.0.2 pretty_dio_logger: ^1.2.0-beta-1 provider: ^6.0.2 + pub_semver: ^2.1.1 share_plus: ^4.0.4 ssh_key: ^0.7.1 system_theme: ^2.0.0