Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: option to delete local secondary when remote has been reset #1033

Open
wants to merge 40 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d9523b2
feat: introduce isSecondaryReset() in AtClientSpec
srieteja Apr 29, 2023
fc7306c
feat: implement isSecondaryReset() and deleteLocalSecondaryStorageWit…
srieteja Apr 29, 2023
aec9bda
test: added tests for the new methods
srieteja Apr 29, 2023
32b98be
Merge branch 'trunk' into reset_local_secondary
srieteja May 15, 2023
7daf11b
build: add dependency_ovveride for at_commons
srieteja May 15, 2023
435f25e
Merge remote-tracking branch 'origin/reset_local_secondary' into rese…
srieteja May 15, 2023
c71d587
Merge branch 'trunk' into reset_local_secondary
srieteja May 15, 2023
702160e
Merge branch 'trunk' into reset_local_secondary
srieteja Jun 7, 2023
f31908d
Merge branch 'trunk' into reset_local_secondary
srieteja Jul 9, 2023
e24604a
use lookup verb builder instead of PLookup verb builder
srieteja Jul 9, 2023
518dc63
fix analyzer issues
srieteja Jul 9, 2023
1f35536
run dart formatter
srieteja Jul 9, 2023
005c6e8
build: introduce at_commons dependency overrides for tests
srieteja Jul 9, 2023
191dfc0
refactor: merge methods _deleteHiveStorage() and _deleteCommitLogStorage
srieteja Jul 19, 2023
b2a8d55
Merge branch 'trunk' into reset_local_secondary
srieteja Jul 19, 2023
bf92190
refactor: isSecondaryReset() now returns Future<bool>
srieteja Jul 19, 2023
216eec8
feat: introduce deleteLocalStorageWithConsent in at_client_spec
srieteja Jul 19, 2023
6b0efb8
feat: deleteLocalStorageWithConsent now fetches hiveStoragePath and c…
srieteja Jul 19, 2023
09c1a40
Merge remote-tracking branch 'origin/reset_local_secondary' into rese…
srieteja Jul 19, 2023
eccd624
test: modify test according to new changes
srieteja Jul 19, 2023
ac018d0
test: modify test according to new changes
srieteja Jul 19, 2023
f7349c9
tests: fix unit tests
srieteja Jul 31, 2023
80e5ab9
feat: introduce and consume menthod to stop instances of commitLog an…
srieteja Jul 31, 2023
4263b82
Update pubspec.yaml
srieteja Aug 16, 2023
17e346f
Update pubspec.yaml
srieteja Aug 16, 2023
a8ec40e
refactor: introduce option to close storage manager
srieteja Aug 16, 2023
5babc71
feat: consume stop storage_manager and improve isReset check
srieteja Aug 16, 2023
c8d9306
Merge branch 'trunk' into reset_local_secondary
srieteja Aug 16, 2023
572db9f
fix: analyzer issues
srieteja Aug 17, 2023
0b6188a
Merge remote-tracking branch 'origin/reset_local_secondary' into rese…
srieteja Aug 17, 2023
bb6abc0
Merge branch 'trunk' into reset_local_secondary
srieteja Sep 4, 2023
22d5654
Merge branch 'trunk' into reset_local_secondary
srieteja Sep 4, 2023
eda3be1
Merge branch 'trunk' into reset_local_secondary
srieteja Sep 5, 2023
c143e5c
Merge branch 'trunk' into reset_local_secondary
srieteja Oct 12, 2023
cbeddcf
Merge branch 'trunk' into reset_local_secondary
srieteja Oct 31, 2023
12ea910
build: remove at_commons dependency override
srieteja Oct 31, 2023
55b4ce2
Merge remote-tracking branch 'origin/reset_local_secondary' into rese…
srieteja Oct 31, 2023
b12f479
chore: fix dart analyzer issues
srieteja Oct 31, 2023
4002f21
Merge branch 'trunk' into reset_local_secondary
srieteja Sep 11, 2024
db4e12a
fix: update usage of PLookupBuilder
srieteja Sep 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions packages/at_client/lib/src/client/at_client_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,113 @@ class AtClientImpl implements AtClient, AtSignChangeListener {
}
}

@override
Future<void> isSecondaryReset() async {
srieteja marked this conversation as resolved.
Show resolved Hide resolved
_logger.finer('Performing Remote Secondary reset check');
var localPublicKey =
await getLocalSecondary()?.getEncryptionPublicKey(getCurrentAtSign()!);

LookupVerbBuilder lookupBuilder = LookupVerbBuilder()
..atKey = 'publickey' //AT_ENCRYPTION_PUBLIC_KEY
..sharedBy = getCurrentAtSign();
String? remotePublicKey =
await getRemoteSecondary()?.executeVerb(lookupBuilder);
// secondary response is in format 'data:publickey'. Removing 'data:' from response
remotePublicKey = remotePublicKey?.replaceFirst('data:', '');

if (localPublicKey.isNull) {
_logger.info('Could not fetch EncryptionPublicKey from LocalSecondary.'
' Unable to complete reset check');
return;
}

// TODO should there be a check for remote enc_key being null ?

if (localPublicKey != remotePublicKey) {
_logger.info('EncryptionPublicKey on LocalSecondary: $localPublicKey');
_logger.info('EncryptionPublicKey on RemoteSecondary: $remotePublicKey');
_logger.shout(
'AtEncryptionPublicKey on local secondary and remote secondary are different.'
'\nThis indicates remote secondary has been reset.'
'\nPlease delete localStorage and restart the client');
_logger.info('To delete localSecondary,'
' call AtClientImpl.deleteLocalSecondary() with user consent');
throw AtResetException('Remote secondary has been reset');
}
_logger.finer('Secondary is not reset. Status ok');
}

void deleteLocalSecondaryStorageWithConsent(
String commitLogDirectoryPath, String hiveStorageDirectoryPath,
{required bool userConsentToDeleteLocalSecondaryStorage}) {
_logger.shout(
'Consent to delete LocalSecondary storage received: $userConsentToDeleteLocalSecondaryStorage');
if (userConsentToDeleteLocalSecondaryStorage) {
_logger
.info('Deleting CommitLog local storage at: $commitLogDirectoryPath');
_deleteLocalCommitLog(commitLogDirectoryPath);
_logger.info('Deleting hive local storage at: $hiveStorageDirectoryPath');
_deleteLocalHiveStorage(hiveStorageDirectoryPath);
}
}

void _deleteLocalCommitLog(String commitLogDirectoryPath) {
srieteja marked this conversation as resolved.
Show resolved Hide resolved
String fileName =
'commit_log_${AtUtils.getShaForAtSign(getCurrentAtSign()!)}.hive';
String lockFileName =
'commit_log_${AtUtils.getShaForAtSign(getCurrentAtSign()!)}.lock';

File commitLogFile = File('$commitLogDirectoryPath/$fileName');

// Todo: Do windows/mac have lock files ?
File lockFile = File('$commitLogDirectoryPath/$lockFileName');

if (commitLogFile.existsSync()) {
commitLogFile.deleteSync();
lockFile.deleteSync();
_logger.info('Successfully deleted commitLog storage');
// Since recursive delete is false by default, If the directory is empty
// the directory will also be deleted
Directory(commitLogDirectoryPath).deleteSync();
return;
}
throw AtClientException.message(
'CommitLog storage not found at path: $commitLogFile.'
' Please provide a valid CommitLog directory path');
}

void _deleteLocalHiveStorage(String hiveStorageDirectoryPath) {
String hashFileName =
'${AtUtils.getShaForAtSign(getCurrentAtSign()!)}.hash';
String hiveFileName =
'${AtUtils.getShaForAtSign(getCurrentAtSign()!)}.hive';
String hiveLockFileName =
'${AtUtils.getShaForAtSign(getCurrentAtSign()!)}.lock';

File hiveFile = File('$hiveStorageDirectoryPath/$hiveFileName');
File hiveLockFile = File('$hiveStorageDirectoryPath/$hiveLockFileName');
File hashFile = File('$hiveStorageDirectoryPath/$hashFileName');

if (hashFile.existsSync() && hiveFile.existsSync()) {
hashFile.deleteSync();
hiveFile.deleteSync();
hiveLockFile.deleteSync();
_logger.info('Successfully deleted hive storage');
// Since recursive delete is true by default, If the directory is empty
// the directory will be deleted too
Directory(hiveStorageDirectoryPath).deleteSync();
} else {
if (!hiveFile.existsSync()) {
throw AtClientException.message(
'hive file not found at path: $hiveFile.'
' Please provide a valid hive storage directory path');
}
throw AtClientException.message(
'hive hash file not found at path: $hiveFile.'
' Please provide a valid hive storage directory path');
}
}

// TODO v4 - remove the follow methods in version 4 of at_client package

@override
Expand Down
4 changes: 4 additions & 0 deletions packages/at_client/lib/src/client/at_client_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,10 @@ abstract class AtClient {
Function streamCompletionCallBack,
Function streamReceiveCallBack);

/// Performs a check to see if RemoteSecondary has been reset
/// throws [AtResetException] if the RemoteSecondary has been reset
Future<void> isSecondaryReset();

/// Uploads list of [files] to filebin and shares the file download url with [sharedWithAtSigns]
/// returns map containing key of each sharedWithAtSign and value of [FileTransferObject]
@Deprecated(
Expand Down
8 changes: 8 additions & 0 deletions packages/at_client/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ dependencies:
meta: ^1.8.0
version: ^3.0.2

dependency_overrides:
srieteja marked this conversation as resolved.
Show resolved Hide resolved
at_commons:
git:
url: https://github.com/atsign-foundation/at_libraries
path: packages/at_commons
ref: at_reset_exception


dev_dependencies:
lints: ^2.0.0
test: ^1.21.4
Expand Down
45 changes: 45 additions & 0 deletions packages/at_client/test/at_client_impl_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:at_client/at_client.dart';
import 'package:at_client/src/compaction/at_commit_log_compaction.dart';
import 'package:at_client/src/service/notification_service_impl.dart';
import 'package:at_client/src/service/sync_service_impl.dart';
import 'package:at_commons/at_builders.dart';
import 'package:at_persistence_secondary_server/at_persistence_secondary_server.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
Expand All @@ -20,6 +21,10 @@ class MockAtCompactionJob extends Mock implements AtCompactionJob {
}
}

class MockRemoteSecondary extends Mock implements RemoteSecondary {}

class MockSecondaryKeystore extends Mock implements SecondaryKeyStore {}

void main() {
group('A group of at client impl create tests', () {
final String atSign = '@alice';
Expand Down Expand Up @@ -250,4 +255,44 @@ void main() {
expect(key.key, 'uppercase'); //key should be converted to lower case
});
});

group('Group of tests verify client behaviour on remote secondary reset', () {
registerFallbackValue(MockRemoteSecondary());
registerFallbackValue(LookupVerbBuilder());

RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
SecondaryKeyStore mockKeystore = MockSecondaryKeystore();
AtClient client;

test('Verify isSecondaryReset() functionality - negative case', () async {
AtData responseObj = AtData()..data = 'incorrectLocalEncPublicKey';
when(() => mockRemoteSecondary.executeVerb(any())).thenAnswer(
(invocation) => Future.value('data:incorrectRemoteEncPublicKey'));
when(() => mockKeystore.get(any()))
.thenAnswer((invocation) => Future.value(responseObj));

client = await AtClientImpl.create('@alice47', 'resetLocalTest',
AtClientPreference()..isLocalStoreRequired = true,
remoteSecondary: mockRemoteSecondary,
localSecondaryKeyStore: mockKeystore);
expect(client.isSecondaryReset(),
throwsA(predicate((dynamic e) => e is AtResetException)));
});

test('Verify isSecondaryReset() functionality - positive case', () async {
AtData responseObj = AtData()..data = 'correctEncPublicKey';
when(() => mockRemoteSecondary.executeVerb(any()))
.thenAnswer((invocation) => Future.value('data:correctEncPublicKey'));
when(() => mockKeystore.get(any()))
.thenAnswer((invocation) => Future.value(responseObj));

client = await AtClientImpl.create('@alice47', 'resetLocalTest',
AtClientPreference()..isLocalStoreRequired = true,
remoteSecondary: mockRemoteSecondary,
localSecondaryKeyStore: mockKeystore);
// errorless execution test
// if the method call below triggers any errors/exceptions this test will fail
await client.isSecondaryReset();
});
});
}
7 changes: 7 additions & 0 deletions tests/at_end2end_test/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ dependencies:
at_client:
path: ../../packages/at_client

dependency_overrides:
at_commons:
git:
url: https://github.com/atsign-foundation/at_libraries
path: packages/at_commons
ref: at_reset_exception

dev_dependencies:
pedantic: ^1.11.1
test: ^1.21.4
Expand Down
7 changes: 7 additions & 0 deletions tests/at_functional_test/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ dependencies:
at_client:
path: ../../packages/at_client

dependency_overrides:
at_commons:
git:
url: https://github.com/atsign-foundation/at_libraries
path: packages/at_commons
ref: at_reset_exception

dev_dependencies:
test: ^1.24.3
lints: ^2.0.0
Expand Down