Add auth functions to server_installation_repository.dart

pull/90/head
Inex Code 2022-05-18 13:39:11 +03:00
parent dd77b99ac8
commit d2553b0d08
13 changed files with 269 additions and 33 deletions

View File

@ -5,14 +5,14 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.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/api_token.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.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/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/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/timezone_settings.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'api_map.dart'; import 'api_map.dart';
@ -34,9 +34,13 @@ class ServerApi extends ApiMap {
bool hasLogger; bool hasLogger;
bool isWithToken; bool isWithToken;
String? overrideDomain; String? overrideDomain;
String? customToken;
ServerApi( ServerApi(
{this.hasLogger = false, this.isWithToken = true, this.overrideDomain}); {this.hasLogger = false,
this.isWithToken = true,
this.overrideDomain,
this.customToken});
BaseOptions get options { BaseOptions get options {
var options = BaseOptions(); var options = BaseOptions();
@ -52,7 +56,12 @@ class ServerApi extends ApiMap {
} }
if (overrideDomain != null) { if (overrideDomain != null) {
options = BaseOptions(baseUrl: 'https://api.$overrideDomain'); options = BaseOptions(
baseUrl: 'https://api.$overrideDomain',
headers: customToken != null
? {'Authorization': 'Bearer $customToken'}
: null,
);
} }
return options; return options;

View File

@ -1,7 +1,12 @@
import 'dart:io';
import 'package:basic_utils/basic_utils.dart'; import 'package:basic_utils/basic_utils.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.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/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.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_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.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/logic/models/message.dart';
import 'package:selfprivacy/ui/components/action_button/action_button.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart';
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
import '../server_installation/server_installation_cubit.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 { class ServerInstallationRepository {
Box box = Hive.box(BNames.serverInstallationBox); Box box = Hive.box(BNames.serverInstallationBox);
@ -43,7 +61,7 @@ class ServerInstallationRepository {
); );
} }
if (getIt<ApiConfigModel>().serverDomain?.provider == DnsProvider.Unknown) { if (serverDomain != null && serverDomain.provider == DnsProvider.Unknown) {
return ServerInstallationRecovery( return ServerInstallationRecovery(
hetznerKey: hetznerToken, hetznerKey: hetznerToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
@ -52,7 +70,8 @@ class ServerInstallationRepository {
serverDetails: serverDetails, serverDetails: serverDetails,
rootUser: box.get(BNames.rootUser), rootUser: box.get(BNames.rootUser),
currentStep: _getCurrentRecoveryStep( currentStep: _getCurrentRecoveryStep(
hetznerToken, cloudflareToken, serverDomain!, serverDetails), hetznerToken, cloudflareToken, serverDomain, serverDetails),
recoveryCapabilities: await getRecoveryCapabilities(serverDomain),
); );
} }
@ -296,6 +315,158 @@ class ServerInstallationRepository {
return await hetznerApi.powerOn(); return await hetznerApi.powerOn();
} }
Future<ServerRecoveryCapabilities> 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<String> 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<String> 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<ServerHostingDetails> 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<ServerHostingDetails> 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<ServerHostingDetails> 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<void> saveServerDetails(ServerHostingDetails serverDetails) async { Future<void> saveServerDetails(ServerHostingDetails serverDetails) async {
await getIt<ApiConfigModel>().storeServerDetails(serverDetails); await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
} }

View File

@ -265,8 +265,15 @@ enum RecoveryStep {
BackblazeToken, BackblazeToken,
} }
enum ServerRecoveryCapabilities {
none,
legacy,
loginTokens,
}
class ServerInstallationRecovery extends ServerInstallationState { class ServerInstallationRecovery extends ServerInstallationState {
final RecoveryStep currentStep; final RecoveryStep currentStep;
final ServerRecoveryCapabilities recoveryCapabilities;
const ServerInstallationRecovery({ const ServerInstallationRecovery({
String? hetznerKey, String? hetznerKey,
@ -276,6 +283,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
User? rootUser, User? rootUser,
ServerHostingDetails? serverDetails, ServerHostingDetails? serverDetails,
required RecoveryStep this.currentStep, required RecoveryStep this.currentStep,
required ServerRecoveryCapabilities this.recoveryCapabilities,
}) : super( }) : super(
hetznerKey: hetznerKey, hetznerKey: hetznerKey,
cloudFlareKey: cloudFlareKey, cloudFlareKey: cloudFlareKey,
@ -309,13 +317,16 @@ class ServerInstallationRecovery extends ServerInstallationState {
User? rootUser, User? rootUser,
ServerHostingDetails? serverDetails, ServerHostingDetails? serverDetails,
RecoveryStep? currentStep, RecoveryStep? currentStep,
ServerRecoveryCapabilities? recoveryCapabilities,
}) => }) =>
ServerInstallationRecovery( ServerInstallationRecovery(
hetznerKey: hetznerKey ?? this.hetznerKey, hetznerKey: hetznerKey ?? this.hetznerKey,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
backblazeCredential: backblazeCredential ?? this.backblazeCredential, backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain, serverDomain: serverDomain ?? this.serverDomain,
rootUser: rootUser ?? this.rootUser, rootUser: rootUser ?? this.rootUser,
serverDetails: serverDetails ?? this.serverDetails, serverDetails: serverDetails ?? this.serverDetails,
currentStep: currentStep ?? this.currentStep); currentStep: currentStep ?? this.currentStep,
recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities,
);
} }

View File

@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_details.dart'; part of 'server_details_screen.dart';
// ************************************************************************** // **************************************************************************
// TypeAdapterGenerator // TypeAdapterGenerator

View File

@ -1,10 +1,10 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.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/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_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/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/logic/models/provider.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.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/helpers/modals.dart';
import 'package:selfprivacy/ui/pages/backup_details/backup_details.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/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'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
var navigatorKey = GlobalKey<NavigatorState>(); var navigatorKey = GlobalKey<NavigatorState>();
@ -113,7 +113,7 @@ class _Card extends StatelessWidget {
context: context, context: context,
builder: (context) => BrandBottomSheet( builder: (context) => BrandBottomSheet(
isExpended: true, isExpended: true,
child: ServerDetails(), child: ServerDetailsScreen(),
), ),
); );

View File

@ -1,4 +1,4 @@
part of 'server_details.dart'; part of 'server_details_screen.dart';
class _Chart extends StatelessWidget { class _Chart extends StatelessWidget {
const _Chart({Key? key}) : super(key: key); const _Chart({Key? key}) : super(key: key);

View File

@ -1,4 +1,4 @@
part of 'server_details.dart'; part of 'server_details_screen.dart';
class _Header extends StatelessWidget { class _Header extends StatelessWidget {
const _Header({ const _Header({

View File

@ -1,11 +1,12 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_colors.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.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/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_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/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.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_radio_tile/brand_radio_tile.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.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: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/components/switch_block/switch_bloc.dart';
import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.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/named_font_weight.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:timezone/timezone.dart'; import 'package:timezone/timezone.dart';
import 'cpu_chart.dart'; import 'cpu_chart.dart';
import 'network_charts.dart'; import 'network_charts.dart';
import 'package:selfprivacy/utils/extensions/duration.dart';
part 'server_settings.dart';
part 'text_details.dart';
part 'chart.dart'; part 'chart.dart';
part 'header.dart'; part 'header.dart';
part 'server_settings.dart';
part 'text_details.dart';
part 'time_zone/time_zone.dart'; part 'time_zone/time_zone.dart';
var navigatorKey = GlobalKey<NavigatorState>(); var navigatorKey = GlobalKey<NavigatorState>();
class ServerDetails extends StatefulWidget { class ServerDetailsScreen extends StatefulWidget {
const ServerDetails({Key? key}) : super(key: key); const ServerDetailsScreen({Key? key}) : super(key: key);
@override @override
_ServerDetailsState createState() => _ServerDetailsState(); _ServerDetailsScreenState createState() => _ServerDetailsScreenState();
} }
class _ServerDetailsState extends State<ServerDetails> class _ServerDetailsScreenState extends State<ServerDetailsScreen>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late TabController tabController; late TabController tabController;

View File

@ -1,4 +1,4 @@
part of 'server_details.dart'; part of 'server_details_screen.dart';
class _ServerSettings extends StatelessWidget { class _ServerSettings extends StatelessWidget {
const _ServerSettings({ const _ServerSettings({

View File

@ -1,4 +1,4 @@
part of 'server_details.dart'; part of 'server_details_screen.dart';
class _TextDetails extends StatelessWidget { class _TextDetails extends StatelessWidget {
const _TextDetails({Key? key}) : super(key: key); const _TextDetails({Key? key}) : super(key: key);

View File

@ -1,4 +1,4 @@
part of '../server_details.dart'; part of '../server_details_screen.dart';
final List<Location> locations = timeZoneDatabase.locations.values.toList() final List<Location> locations = timeZoneDatabase.locations.values.toList()
..sort((l1, l2) => ..sort((l1, l2) =>

View File

@ -218,6 +218,48 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.3" 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: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -750,7 +792,7 @@ packages:
source: hosted source: hosted
version: "6.0.2" version: "6.0.2"
pub_semver: pub_semver:
dependency: transitive dependency: "direct main"
description: description:
name: pub_semver name: pub_semver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"

View File

@ -13,6 +13,7 @@ dependencies:
crypt: ^4.2.1 crypt: ^4.2.1
cubit_form: ^2.0.1 cubit_form: ^2.0.1
cupertino_icons: ^1.0.4 cupertino_icons: ^1.0.4
device_info_plus: ^3.2.3
dio: ^4.0.4 dio: ^4.0.4
dynamic_color: ^1.2.2 dynamic_color: ^1.2.2
easy_localization: ^3.0.0 easy_localization: ^3.0.0
@ -36,6 +37,7 @@ dependencies:
package_info: ^2.0.2 package_info: ^2.0.2
pretty_dio_logger: ^1.2.0-beta-1 pretty_dio_logger: ^1.2.0-beta-1
provider: ^6.0.2 provider: ^6.0.2
pub_semver: ^2.1.1
share_plus: ^4.0.4 share_plus: ^4.0.4
ssh_key: ^0.7.1 ssh_key: ^0.7.1
system_theme: ^2.0.0 system_theme: ^2.0.0