ente/auth/lib/store/code_store.dart

166 lines
5.2 KiB
Dart
Raw Normal View History

2022-11-01 06:13:06 +00:00
import 'dart:convert';
2023-07-31 10:17:54 +00:00
import 'package:collection/collection.dart';
import 'package:ente_auth/core/configuration.dart';
2022-11-01 06:13:06 +00:00
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/models/authenticator/entity_result.dart';
2022-11-01 06:13:06 +00:00
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/store/offline_authenticator_db.dart';
import 'package:logging/logging.dart';
2022-11-01 06:13:06 +00:00
class CodeStore {
static final CodeStore instance = CodeStore._privateConstructor();
CodeStore._privateConstructor();
late AuthenticatorService _authenticatorService;
final _logger = Logger("CodeStore");
2022-11-01 06:13:06 +00:00
Future<void> init() async {
_authenticatorService = AuthenticatorService.instance;
}
2023-09-04 10:31:59 +00:00
Future<List<Code>> getAllCodes({AccountMode? accountMode}) async {
final mode = accountMode ?? _authenticatorService.getAccountMode();
final List<EntityResult> entities =
2023-09-04 10:31:59 +00:00
await _authenticatorService.getEntities(mode);
2022-11-01 06:13:06 +00:00
final List<Code> codes = [];
for (final entity in entities) {
final decodeJson = jsonDecode(entity.rawData);
2022-11-01 06:13:06 +00:00
final code = Code.fromRawData(decodeJson);
2022-11-22 08:07:09 +00:00
code.generatedID = entity.generatedID;
code.hasSynced = entity.hasSynced;
2022-11-01 06:13:06 +00:00
codes.add(code);
}
2023-05-11 08:48:11 +00:00
// sort codes by issuer,account
codes.sort((a, b) {
2023-07-31 10:17:54 +00:00
final issuerComparison = compareAsciiLowerCaseNatural(a.issuer, b.issuer);
2023-05-11 08:48:11 +00:00
if (issuerComparison != 0) {
return issuerComparison;
}
2023-07-31 10:17:54 +00:00
return compareAsciiLowerCaseNatural(a.account, b.account);
2022-11-14 16:21:25 +00:00
});
2022-11-01 06:13:06 +00:00
return codes;
}
2023-09-05 09:18:23 +00:00
Future<AddResult> addCode(
Code code, {
bool shouldSync = true,
2023-09-04 10:31:59 +00:00
AccountMode? accountMode,
}) async {
final mode = accountMode ?? _authenticatorService.getAccountMode();
2023-09-04 10:31:59 +00:00
final codes = await getAllCodes(accountMode: mode);
2022-11-22 07:19:39 +00:00
bool isExistingCode = false;
for (final existingCode in codes) {
if (existingCode == code) {
_logger.info("Found duplicate code, skipping add");
2023-09-05 09:18:23 +00:00
return AddResult.duplicate;
2022-11-22 08:07:09 +00:00
} else if (existingCode.generatedID == code.generatedID) {
2022-11-22 07:19:39 +00:00
isExistingCode = true;
break;
}
}
2023-09-05 09:18:23 +00:00
late AddResult result;
2022-11-22 07:19:39 +00:00
if (isExistingCode) {
2023-09-05 09:18:23 +00:00
result = AddResult.updateCode;
2022-11-22 07:19:39 +00:00
await _authenticatorService.updateEntry(
2022-11-22 08:07:09 +00:00
code.generatedID!,
2022-11-22 07:19:39 +00:00
jsonEncode(code.rawData),
shouldSync,
2023-09-04 10:31:59 +00:00
mode,
2022-11-22 07:19:39 +00:00
);
} else {
2023-09-05 09:18:23 +00:00
result = AddResult.newCode;
2022-11-22 08:07:09 +00:00
code.generatedID = await _authenticatorService.addEntry(
2022-11-22 07:19:39 +00:00
jsonEncode(code.rawData),
shouldSync,
2023-09-04 10:31:59 +00:00
mode,
2022-11-22 07:19:39 +00:00
);
}
2022-11-01 06:13:06 +00:00
Bus.instance.fire(CodesUpdatedEvent());
2023-09-05 09:18:23 +00:00
return result;
2022-11-01 06:13:06 +00:00
}
2023-09-04 10:31:59 +00:00
Future<void> removeCode(Code code, {AccountMode? accountMode}) async {
final mode = accountMode ?? _authenticatorService.getAccountMode();
await _authenticatorService.deleteEntry(code.generatedID!, mode);
2022-11-01 06:13:06 +00:00
Bus.instance.fire(CodesUpdatedEvent());
}
bool _isOfflineImportRunning = false;
Future<void> importOfflineCodes() async {
if(_isOfflineImportRunning) {
return;
}
_isOfflineImportRunning = true;
2023-09-05 09:18:23 +00:00
Logger logger = Logger('importOfflineCodes');
try {
Configuration config = Configuration.instance;
2023-09-05 02:51:11 +00:00
if (!config.hasConfiguredAccount() ||
!config.hasOptedForOfflineMode() ||
config.getOfflineSecretKey() == null) {
return;
}
2023-09-05 09:18:23 +00:00
logger.info('start import');
2023-09-05 02:51:11 +00:00
List<Code> offlineCodes = await CodeStore.instance
.getAllCodes(accountMode: AccountMode.offline);
if (offlineCodes.isEmpty) {
2023-09-05 02:51:11 +00:00
return;
}
2023-09-05 02:42:52 +00:00
bool isOnlineSyncDone = await AuthenticatorService.instance.onlineSync();
if (!isOnlineSyncDone) {
2023-09-05 09:18:23 +00:00
logger.info("skip as online sync is not done");
2023-09-05 02:42:52 +00:00
return;
}
final List<Code> onlineCodes =
await CodeStore.instance.getAllCodes(accountMode: AccountMode.online);
2023-09-05 09:18:23 +00:00
logger.info(
'importing ${offlineCodes.length} offline codes with ${onlineCodes.length} online codes',
);
for (Code eachCode in offlineCodes) {
bool alreadyPresent = onlineCodes.any(
(oc) =>
oc.issuer == eachCode.issuer &&
oc.account == eachCode.account &&
oc.secret == eachCode.secret,
);
int? generatedID = eachCode.generatedID!;
2023-09-05 09:18:23 +00:00
logger.info(
'importingCode: genID ${eachCode.generatedID} & isAlreadyPresent $alreadyPresent',
);
if (!alreadyPresent) {
// Avoid conflict with generatedID of online codes
eachCode.generatedID = null;
2023-09-05 09:18:23 +00:00
final AddResult result = await CodeStore.instance.addCode(
eachCode,
accountMode: AccountMode.online,
shouldSync: false,
);
2023-09-05 09:18:23 +00:00
logger.info(
'importedCode: genID ${eachCode.generatedID} result: ${result.name}',
);
}
2023-09-05 02:42:52 +00:00
await OfflineAuthenticatorDB.instance.deleteByIDs(
generatedIDs: [generatedID],
2023-09-05 02:42:52 +00:00
);
}
AuthenticatorService.instance.onlineSync().ignore();
} catch (e, s) {
_logger.severe("error while importing offline codes", e, s);
} finally {
_isOfflineImportRunning = false;
}
}
2022-11-01 06:13:06 +00:00
}
2023-09-05 09:18:23 +00:00
enum AddResult {
newCode,
duplicate,
updateCode,
}