Skip to content

Commit

Permalink
TW-2220: Add new func to mapping Fed server data from well-know
Browse files Browse the repository at this point in the history
  • Loading branch information
nqhhdev committed Feb 5, 2025
1 parent 3956877 commit a3072a7
Show file tree
Hide file tree
Showing 16 changed files with 530 additions and 30 deletions.
12 changes: 12 additions & 0 deletions lib/data/datasource/federation_configurations_datasource.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:fluffychat/data/model/federation_server/federation_configuration.dart';

abstract class FederationConfigurationsDatasource {
Future<FederationConfigurations> getFederationConfigurations(String userId);

Future<void> saveFederationConfigurations(
String userId,
FederationConfigurations federationConfiguration,
);

Future<void> deleteFederationConfigurations(String userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'package:fluffychat/data/datasource/federation_configurations_datasource.dart';
import 'package:fluffychat/data/hive/dto/federation_configurations_hive_obj.dart';
import 'package:fluffychat/data/hive/hive_collection_federation_database.dart';
import 'package:fluffychat/data/model/federation_server/federation_configuration.dart';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/exception/federation_configuration_not_found.dart';
import 'package:matrix/matrix.dart';

class HiveFederationConfigurationsDatasourceImpl
implements FederationConfigurationsDatasource {
@override
Future<FederationConfigurations> getFederationConfigurations(
String userId,
) async {
final hiveCollectionFederationDatabase =
await getIt.getAsync<HiveCollectionFederationDatabase>();
final cachedFederationConfigurations =
await hiveCollectionFederationDatabase.federationConfigurationsBox
.get(userId);
if (cachedFederationConfigurations != null) {
final federationConfigurationsHiveObj =
FederationConfigurationsHiveObj.fromJson(
copyMap(
cachedFederationConfigurations,
),
);

return FederationConfigurations(
fedServerInformation: federationConfigurationsHiveObj
.federationServerInformation
.toFederationServerInformation(),
identityServerInformation:
federationConfigurationsHiveObj.identityServerUrl != null
? IdentityServerInformation(
baseUrl: Uri.parse(
federationConfigurationsHiveObj.identityServerUrl!,
),
)
: null,
);
}
throw FederationConfigurationNotFound();
}

@override
Future<void> saveFederationConfigurations(
String userId,
FederationConfigurations federationConfiguration,
) async {
final hiveCollectionFederationDatabase =
await getIt.getAsync<HiveCollectionFederationDatabase>();
return hiveCollectionFederationDatabase.federationConfigurationsBox.put(
userId,
FederationConfigurationsHiveObj.fromFederationConfigurations(
federationConfiguration,
).toJson(),
);
}

@override
Future<void> deleteFederationConfigurations(String userId) async {
final hiveCollectionFederationDatabase =
getIt.get<HiveCollectionFederationDatabase>();
return hiveCollectionFederationDatabase.federationConfigurationsBox.delete(
userId,
);
}
}
44 changes: 44 additions & 0 deletions lib/data/hive/dto/federation_configurations_hive_obj.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:equatable/equatable.dart';
import 'package:fluffychat/data/hive/dto/federation_server_information_hive_obj.dart';
import 'package:fluffychat/data/model/federation_server/federation_configuration.dart';
import 'package:json_annotation/json_annotation.dart';

part 'federation_configurations_hive_obj.g.dart';

@JsonSerializable(explicitToJson: true)
class FederationConfigurationsHiveObj with EquatableMixin {
final FederationServerInformationHiveObj federationServerInformation;

final String? identityServerUrl;

FederationConfigurationsHiveObj({
required this.federationServerInformation,
required this.identityServerUrl,
});

factory FederationConfigurationsHiveObj.fromFederationConfigurations(
FederationConfigurations federationConfigurations,
) {
return FederationConfigurationsHiveObj(
federationServerInformation:
FederationServerInformationHiveObj.fromFederationServerInformation(
federationConfigurations.fedServerInformation,
),
identityServerUrl: federationConfigurations
.identityServerInformation?.baseUrl
.toString(),
);
}

factory FederationConfigurationsHiveObj.fromJson(Map<String, dynamic> json) =>
_$FederationConfigurationsHiveObjFromJson(json);

Map<String, dynamic> toJson() =>
_$FederationConfigurationsHiveObjToJson(this);

@override
List<Object?> get props => [
federationServerInformation,
identityServerUrl,
];
}
40 changes: 40 additions & 0 deletions lib/data/hive/dto/federation_server_information_hive_obj.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:equatable/equatable.dart';
import 'package:fluffychat/data/model/federation_server/federation_server_information.dart';
import 'package:json_annotation/json_annotation.dart';

part 'federation_server_information_hive_obj.g.dart';

@JsonSerializable(explicitToJson: true)
class FederationServerInformationHiveObj with EquatableMixin {
final List<String>? baseUrls;

FederationServerInformationHiveObj({
required this.baseUrls,
});

factory FederationServerInformationHiveObj.fromJson(
Map<String, dynamic> json,
) =>
_$FederationServerInformationHiveObjFromJson(json);

Map<String, dynamic> toJson() =>
_$FederationServerInformationHiveObjToJson(this);

FederationServerInformation toFederationServerInformation() {
return FederationServerInformation(
baseUrls: baseUrls?.map((e) => Uri.parse(e)).toList(),
);
}

factory FederationServerInformationHiveObj.fromFederationServerInformation(
FederationServerInformation fedServerInformation,
) {
return FederationServerInformationHiveObj(
baseUrls:
fedServerInformation.baseUrls?.map((e) => e.toString()).toList(),
);
}

@override
List<Object?> get props => [baseUrls];
}
136 changes: 136 additions & 0 deletions lib/data/hive/hive_collection_federation_database.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import 'dart:convert';
import 'dart:io';

import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';

class HiveCollectionFederationDatabase {
final String name;
final String? path;
final HiveCipher? key;

late BoxCollection _collection;

String get _federationConfigurationsBoxName =>
'federation_configurations_box';
late CollectionBox<Map> federationConfigurationsBox;

HiveCollectionFederationDatabase(this.name, this.path, {this.key});

static Future<HiveCollectionFederationDatabase> databaseBuilder() async {
Logs().d('Open Hive for Federation...');
HiveAesCipher? hiverCipher;
try {
// Workaround for secure storage is calling Platform.operatingSystem on web
if (kIsWeb || PlatformInfos.isIOS) {
// ignore: unawaited_futures
throw MissingPluginException();
}

const secureStorage = FlutterSecureStorage();
final containsEncryptionKey = await secureStorage.read(
key: FlutterHiveCollectionsDatabase.cipherStorageKey,
) !=
null;
if (!containsEncryptionKey) {
// do not try to create a buggy secure storage for new Linux users
if (Platform.isLinux) throw MissingPluginException();
final key = Hive.generateSecureKey();
await secureStorage.write(
key: FlutterHiveCollectionsDatabase.cipherStorageKey,
value: base64UrlEncode(key),
);
}

// workaround for if we just wrote to the key and it still doesn't exist
final rawEncryptionKey = await secureStorage.read(
key: FlutterHiveCollectionsDatabase.cipherStorageKey,
);
if (rawEncryptionKey == null) throw MissingPluginException();

hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey));
} on MissingPluginException catch (_) {
const FlutterSecureStorage()
.delete(key: FlutterHiveCollectionsDatabase.cipherStorageKey)
.catchError((_) {});
Logs().i('Hive encryption is not supported on this platform');
} catch (e, s) {
const FlutterSecureStorage()
.delete(key: FlutterHiveCollectionsDatabase.cipherStorageKey)
.catchError((_) {});
Logs().w('Unable to init Hive encryption', e, s);
}

final db = HiveCollectionFederationDatabase(
'hive_collections_federation',
await _findDatabasePath(),
key: hiverCipher,
);
try {
await db.open();
} catch (e, s) {
Logs().w('Unable to open Federation Hive.', e, s);
const FlutterSecureStorage()
.delete(key: FlutterHiveCollectionsDatabase.cipherStorageKey);
await db.clear().catchError((_) {});
await Hive.deleteFromDisk();
rethrow;
}
Logs().d('Hive for Federation is ready');
return db;
}

static Future<String> _findDatabasePath({
String? path = 'federation_db',
}) async {
if (!kIsWeb) {
Directory directory;
try {
if (Platform.isLinux) {
directory = await getApplicationSupportDirectory();
} else {
directory = await getApplicationDocumentsDirectory();
}
} catch (_) {
try {
directory = await getLibraryDirectory();
} catch (_) {
directory = Directory.current;
}
}
// do not destroy your stable FluffyChat in debug mode
directory = Directory(
directory.uri.resolve(kDebugMode ? 'hive_debug' : 'hive').toFilePath(),
);
directory.create(recursive: true);
path = directory.path;
}
return path!;
}

Future<void> open() async {
_collection = await BoxCollection.open(
name,
{_federationConfigurationsBoxName},
path: path,
key: key,
);
federationConfigurationsBox = await _collection.openBox(
_federationConfigurationsBoxName,
preload: true,
);
}

Future<void> clear() async {
await federationConfigurationsBox.clear();
if (PlatformInfos.isMobile) {
await _collection.deleteFromDisk();
}
}
}
12 changes: 3 additions & 9 deletions lib/data/model/federation_server/federation_configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@ import 'package:equatable/equatable.dart';
import 'package:fluffychat/data/model/federation_server/federation_server_information.dart';
import 'package:matrix/matrix.dart';

class FedConfiguration with EquatableMixin {
final FedServerInformation fedServerInformation;
class FederationConfigurations with EquatableMixin {
final FederationServerInformation fedServerInformation;
final IdentityServerInformation? identityServerInformation;
final String? authUrl;
final LoginType? loginType;

FedConfiguration({
FederationConfigurations({
required this.fedServerInformation,
this.identityServerInformation,
this.authUrl,
this.loginType,
});

@override
List<Object?> get props => [
fedServerInformation,
identityServerInformation,
authUrl,
loginType,
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@ import 'package:json_annotation/json_annotation.dart';
part 'federation_server_information.g.dart';

@JsonSerializable()
class FedServerInformation with EquatableMixin {
class FederationServerInformation with EquatableMixin {
static const String fedServerKey = 'm.federated_identity_services';

@JsonKey(name: 'base_url')
final Uri? baseUrl;
@JsonKey(name: 'base_urls')
final List<Uri>? baseUrls;

@JsonKey(name: 'server_name')
final String? serverName;

FedServerInformation({
this.baseUrl,
this.serverName,
FederationServerInformation({
this.baseUrls,
});

factory FedServerInformation.fromJson(Map<String, dynamic> json) =>
_$FedServerInformationFromJson(json);
factory FederationServerInformation.fromJson(Map<String, dynamic> json) =>
_$FederationServerInformationFromJson(json);

Map<String, dynamic> toJson() => _$FedServerInformationToJson(this);
Map<String, dynamic> toJson() => _$FederationServerInformationToJson(this);

@override
List<Object?> get props => [baseUrl, serverName];
List<Object?> get props => [baseUrls];
}
Loading

0 comments on commit a3072a7

Please sign in to comment.