From 4b1acab87b242eeb41ebae2ec6b997fdfaf8844e Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Tue, 4 Apr 2023 10:59:14 +0530 Subject: [PATCH 1/5] fix: Populate existing metadata for null values of new metadata --- .../CHANGELOG.md | 2 + .../lib/src/model/at_metadata_builder.dart | 101 +++-- .../pubspec.yaml | 2 +- .../test/at_metadata_test.dart | 355 ++++++++++++++++-- 4 files changed, 383 insertions(+), 77 deletions(-) diff --git a/packages/at_persistence_secondary_server/CHANGELOG.md b/packages/at_persistence_secondary_server/CHANGELOG.md index 0d7597c68..f6ab47d40 100644 --- a/packages/at_persistence_secondary_server/CHANGELOG.md +++ b/packages/at_persistence_secondary_server/CHANGELOG.md @@ -1,3 +1,5 @@ +## 3.0.53 +- fix: Modify the AtMetadataBuilder to preserve the existing metadata when not set. Reset the metadata when explicitly set to null. ## 3.0.52 - feat: Add new encryption metadata fields to core persistence classes ## 3.0.51 diff --git a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart index 43ec85814..8bc3faf09 100644 --- a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart +++ b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart @@ -4,10 +4,12 @@ import 'package:at_utils/at_logger.dart'; /// Builder class to build [AtMetaData] object. class AtMetadataBuilder { late AtMetaData atMetaData; + /// We will constrain to millisecond precision because Hive only stores /// [DateTime]s to millisecond precision - see https://github.com/hivedb/hive/issues/474 /// for details. - var currentUtcTimeToMillisecondPrecision = DateTime.now().toUtcMillisecondsPrecision(); + var currentUtcTimeToMillisecondPrecision = + DateTime.now().toUtcMillisecondsPrecision(); static final AtSignLogger logger = AtSignLogger('AtMetadataBuilder'); @@ -16,26 +18,26 @@ class AtMetadataBuilder { /// ttb : Time to birth of the key. If ttb is null, atMetadata's ttb is assigned to ttb. /// ttr : Time to refresh of the key. If ttr is null, atMetadata's ttr is assigned to ttr. /// ccd : Cascade delete. If ccd is null, atMetadata's ccd is assigned to ccd. - AtMetadataBuilder( - {String? atSign, - AtMetaData? newAtMetaData, - AtMetaData? existingMetaData, - int? ttl, - int? ttb, - int? ttr, - bool? ccd, - bool? isBinary, - bool? isEncrypted, - String? dataSignature, - String? sharedKeyEncrypted, - String? publicKeyChecksum, - String? encoding, - String? encKeyName, - String? encAlgo, - String? ivNonce, - String? skeEncKeyName, - String? skeEncAlgo, - }) { + AtMetadataBuilder({ + String? atSign, + AtMetaData? newAtMetaData, + AtMetaData? existingMetaData, + int? ttl, + int? ttb, + int? ttr, + bool? ccd, + bool? isBinary, + bool? isEncrypted, + String? dataSignature, + String? sharedKeyEncrypted, + String? publicKeyChecksum, + String? encoding, + String? encKeyName, + String? encAlgo, + String? ivNonce, + String? skeEncKeyName, + String? skeEncAlgo, + }) { newAtMetaData ??= AtMetaData(); atMetaData = newAtMetaData; // createdAt indicates the date and time of the key created. @@ -58,20 +60,10 @@ class AtMetadataBuilder { ? atMetaData.version = 0 : atMetaData.version = (existingMetaData!.version! + 1); - //If new metadata is available, consider new metadata, else if existing metadata is available consider it. ttl ??= newAtMetaData.ttl; - if (ttl == null && existingMetaData != null) ttl = existingMetaData.ttl; - ttb ??= newAtMetaData.ttb; - if (ttb == null && existingMetaData != null) ttb = existingMetaData.ttb; - ttr ??= newAtMetaData.ttr; - if (ttr == null && existingMetaData != null) ttr = existingMetaData.ttr; - ccd ??= newAtMetaData.isCascade; - if (ccd == null && existingMetaData != null) { - ccd = existingMetaData.isCascade; - } isBinary ??= newAtMetaData.isBinary; isEncrypted ??= newAtMetaData.isEncrypted; dataSignature ??= newAtMetaData.dataSignature; @@ -108,29 +100,34 @@ class AtMetadataBuilder { atMetaData.ivNonce = ivNonce; atMetaData.skeEncKeyName = skeEncKeyName; atMetaData.skeEncAlgo = skeEncAlgo; + + atMetaData = _setNullOrExistingMetadata(existingMetaData); } void setTTL(int? ttl, {int? ttb}) { if (ttl != null) { atMetaData.ttl = ttl; - atMetaData.expiresAt = - _getExpiresAt(currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttl, ttb: ttb); + atMetaData.expiresAt = _getExpiresAt( + currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttl, + ttb: ttb); } } void setTTB(int? ttb) { if (ttb != null) { atMetaData.ttb = ttb; - atMetaData.availableAt = - _getAvailableAt(currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttb); - logger.finer('setTTB($ttb) - set availableAt to ${atMetaData.availableAt}'); + atMetaData.availableAt = _getAvailableAt( + currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttb); + logger + .finer('setTTB($ttb) - set availableAt to ${atMetaData.availableAt}'); } } void setTTR(int? ttr) { if (ttr != null) { atMetaData.ttr = ttr; - atMetaData.refreshAt = _getRefreshAt(currentUtcTimeToMillisecondPrecision, ttr); + atMetaData.refreshAt = + _getRefreshAt(currentUtcTimeToMillisecondPrecision, ttr); } } @@ -163,6 +160,36 @@ class AtMetadataBuilder { return today.add(Duration(seconds: ttr)); } + /// If metadata contains "null" string, then reset the metadata. So set it to null + /// If metadata contains null (null object), then fetch the existing metadata.If + /// existing metadata value is not null, set it the current AtMetaData obj. + AtMetaData _setNullOrExistingMetadata(AtMetaData? existingAtMetadata) { + if (existingAtMetadata == null) { + return atMetaData; + } + var atMetaDataJson = atMetaData.toJson(); + var existingAtMetaDataJson = existingAtMetadata.toJson(); + atMetaDataJson.forEach((key, value) { + switch (value) { + // If command does not contains the attributes of a metadata, then regex named + // group, inserts null. For a key, if an attribute has a value in previously, + // fetch the value and update it. + case null: + if (existingAtMetaDataJson[key] != null) { + atMetaDataJson[key] = existingAtMetaDataJson[key]; + } + break; + // In the command, if an attribute is explicitly set to null, then verbParams + // contains String value "null". Then reset the metadata. So, set it to null + case 'null': + atMetaDataJson[key] = null; + break; + } + }); + + return AtMetaData.fromJson(atMetaDataJson); + } + AtMetaData build() { return atMetaData; } diff --git a/packages/at_persistence_secondary_server/pubspec.yaml b/packages/at_persistence_secondary_server/pubspec.yaml index 60f15aa28..30706d9a7 100644 --- a/packages/at_persistence_secondary_server/pubspec.yaml +++ b/packages/at_persistence_secondary_server/pubspec.yaml @@ -1,6 +1,6 @@ name: at_persistence_secondary_server description: A Dart library with the implementation classes for the persistence layer of the secondary server. -version: 3.0.52 +version: 3.0.53 repository: https://github.com/atsign-foundation/at_server homepage: https://atsign.dev diff --git a/packages/at_persistence_secondary_server/test/at_metadata_test.dart b/packages/at_persistence_secondary_server/test/at_metadata_test.dart index a226b8b82..f8a824c77 100644 --- a/packages/at_persistence_secondary_server/test/at_metadata_test.dart +++ b/packages/at_persistence_secondary_server/test/at_metadata_test.dart @@ -119,7 +119,8 @@ void main() async { key, AtData()..data = '9878123322', AtMetaData()); // Update the same key var updateKeyDateTime = DateTime.now().toUtcMillisecondsPrecision(); - await hiveKeyStore?.putAll(key, AtData()..data = '9878123322', AtMetaData()..ttl = 10000); + await hiveKeyStore?.putAll( + key, AtData()..data = '9878123322', AtMetaData()..ttl = 10000); var atData = await hiveKeyStore?.get(key); expect(atData?.data, '9878123322'); expect( @@ -156,18 +157,23 @@ void main() async { group('Test json round-tripping', () { test('Test without null values', () { final Metadata startMetaData = Metadata() - ..ttl=100..ttb=200..ttr=3600 - ..ccd=true..isBinary=false..isEncrypted=true - ..dataSignature='dataSignature' - ..pubKeyCS='pubKeyChecksum' - ..sharedKeyEnc='sharedKeyEncrypted' - ..encoding='someEncoding' - ..encKeyName='someEncKeyName' - ..encAlgo='AES/CTR/PKCS7Padding' - ..ivNonce='someIvNonce' - ..skeEncKeyName='someSkeEncKeyName' - ..skeEncAlgo='someSkeEncAlgo'; - final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl = 100 + ..ttb = 200 + ..ttr = 3600 + ..ccd = true + ..isBinary = false + ..isEncrypted = true + ..dataSignature = 'dataSignature' + ..pubKeyCS = 'pubKeyChecksum' + ..sharedKeyEnc = 'sharedKeyEncrypted' + ..encoding = 'someEncoding' + ..encKeyName = 'someEncKeyName' + ..encAlgo = 'AES/CTR/PKCS7Padding' + ..ivNonce = 'someIvNonce' + ..skeEncKeyName = 'someSkeEncKeyName' + ..skeEncAlgo = 'someSkeEncAlgo'; + final AtMetaData startAtMetaData = + AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -177,20 +183,25 @@ void main() async { final Metadata endMetaData = endAtMetaData.toCommonsMetadata(); expect(endMetaData, startMetaData); }); - test ('Test with null values', () { + test('Test with null values', () { final Metadata startMetaData = Metadata() - ..ttl=null..ttb=null..ttr=null - ..ccd=false..isBinary=true..isEncrypted=false - ..dataSignature=null - ..pubKeyCS=null - ..sharedKeyEnc=null - ..encoding=null - ..encKeyName=null - ..encAlgo=null - ..ivNonce=null - ..skeEncKeyName=null - ..skeEncAlgo=null; - final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl = null + ..ttb = null + ..ttr = null + ..ccd = false + ..isBinary = true + ..isEncrypted = false + ..dataSignature = null + ..pubKeyCS = null + ..sharedKeyEnc = null + ..encoding = null + ..encKeyName = null + ..encAlgo = null + ..ivNonce = null + ..skeEncKeyName = null + ..skeEncAlgo = null; + final AtMetaData startAtMetaData = + AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -200,20 +211,25 @@ void main() async { final Metadata endMetaData = endAtMetaData.toCommonsMetadata(); expect(endMetaData, startMetaData); }); - test ('Test with some null, some non-null values', () { + test('Test with some null, some non-null values', () { final Metadata startMetaData = Metadata() - ..ttl=0..ttb=0..ttr=0 - ..ccd=false..isBinary=true..isEncrypted=false - ..dataSignature='foo' - ..pubKeyCS=null - ..sharedKeyEnc=null - ..encoding='base64' - ..encKeyName='someEncKeyName' - ..encAlgo='AES/CTR/PKCS7Padding' - ..ivNonce='someIvOrNonce' - ..skeEncKeyName=null - ..skeEncAlgo=null; - final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl = 0 + ..ttb = 0 + ..ttr = 0 + ..ccd = false + ..isBinary = true + ..isEncrypted = false + ..dataSignature = 'foo' + ..pubKeyCS = null + ..sharedKeyEnc = null + ..encoding = 'base64' + ..encKeyName = 'someEncKeyName' + ..encAlgo = 'AES/CTR/PKCS7Padding' + ..ivNonce = 'someIvOrNonce' + ..skeEncKeyName = null + ..skeEncAlgo = null; + final AtMetaData startAtMetaData = + AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -224,6 +240,267 @@ void main() async { expect(endMetaData, startMetaData); }); }); + + group('A group of tests to assert metadata when key is updated', () { + setUpAll(() async => await setUpFunc(storageDir)); + + test( + 'A test to verify existing metadata is retained when key is updated using put method', + () async { + var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore(atSign)! + .getSecondaryKeyStore(); + String key = '@bob:phone@alice'; + String value = '9878123321'; + AtMetaData atMetaData = AtMetaData() + ..ttl = 100 + ..ttb = 100 + ..ttr = 1000 + ..isCascade = true + ..isBinary = true + ..isEncrypted = true + ..dataSignature = 'dummy_data_signature' + ..sharedKeyEnc = 'dummy_shared_key_env' + ..pubKeyCS = 'dummy_public_key_cs' + ..encoding = 'base64' + ..encKeyName = 'dummy_enc_key' + ..encAlgo = 'rsa' + ..ivNonce = 'dummy_ivnonce' + ..skeEncKeyName = 'dummy_ske' + ..skeEncAlgo = 'dummy_ske_enc_algo'; + + AtData atData = AtData() + ..data = value + ..metaData = atMetaData; + await hiveKeyStore?.put(key, atData); + // Update the key with a no metadata + AtMetaData newAtMetaData = AtMetaData(); + AtData newAtData = AtData() + ..data = value + ..metaData = newAtMetaData; + await hiveKeyStore?.put(key, newAtData); + + AtData? atDataResponse = await hiveKeyStore?.get(key); + expect(atDataResponse?.metaData?.ttl, 100); + expect(atDataResponse?.metaData?.ttb, 100); + expect(atDataResponse?.metaData?.ttr, 1000); + expect(atDataResponse?.metaData?.isCascade, true); + expect(atDataResponse?.metaData?.isBinary, true); + expect(atDataResponse?.metaData?.isEncrypted, true); + expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); + expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); + expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); + expect(atDataResponse?.metaData?.encoding, 'base64'); + expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); + expect(atDataResponse?.metaData?.encAlgo, 'rsa'); + expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); + expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); + expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); + }); + + test( + 'A test to verify new metadata overwrites the existing metadata using put method', + () async { + var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore(atSign)! + .getSecondaryKeyStore(); + + String key = '@bob:phone@alice'; + String value = '9878123321'; + AtMetaData atMetaData = AtMetaData() + ..ttl = 10 + ..ttb = 10 + ..ttr = 10 + ..isCascade = false + ..isBinary = false + ..isEncrypted = false + ..dataSignature = 'dummy_data_signature_old' + ..sharedKeyEnc = 'dummy_shared_key_env_old' + ..pubKeyCS = 'dummy_public_key_cs_old' + ..encoding = 'base64_old' + ..encKeyName = 'dummy_enc_key_old' + ..encAlgo = 'rsa_old' + ..ivNonce = 'dummy_ivnonce_old' + ..skeEncKeyName = 'dummy_ske_old' + ..skeEncAlgo = 'dummy_ske_enc_algo_old'; + AtData atData = AtData() + ..data = value + ..metaData = atMetaData; + await hiveKeyStore?.put(key, atData); + + // Update the key with a new metadata + AtMetaData newAtMetaData = AtMetaData() + ..ttl = 100 + ..ttb = 100 + ..ttr = 1000 + ..isCascade = true + ..isBinary = true + ..isEncrypted = true + ..dataSignature = 'dummy_data_signature' + ..sharedKeyEnc = 'dummy_shared_key_env' + ..pubKeyCS = 'dummy_public_key_cs' + ..encoding = 'base64' + ..encKeyName = 'dummy_enc_key' + ..encAlgo = 'rsa' + ..ivNonce = 'dummy_ivnonce' + ..skeEncKeyName = 'dummy_ske' + ..skeEncAlgo = 'dummy_ske_enc_algo'; + + AtData newAtData = AtData() + ..data = value + ..metaData = newAtMetaData; + await hiveKeyStore?.put(key, newAtData); + + AtData? atDataResponse = await hiveKeyStore?.get(key); + expect(atDataResponse?.metaData?.ttl, 100); + expect(atDataResponse?.metaData?.ttb, 100); + expect(atDataResponse?.metaData?.ttr, 1000); + expect(atDataResponse?.metaData?.isCascade, true); + expect(atDataResponse?.metaData?.isBinary, true); + expect(atDataResponse?.metaData?.isEncrypted, true); + expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); + expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); + expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); + expect(atDataResponse?.metaData?.encoding, 'base64'); + expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); + expect(atDataResponse?.metaData?.encAlgo, 'rsa'); + expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); + expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); + expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); + }); + + test( + 'A test to verify new metadata is applied for fields that are modified and existing metadata is retained for the fields that are not updated', + () async { + var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore(atSign)! + .getSecondaryKeyStore(); + + String key = '@bob:phone@alice'; + String value = '9878123321'; + AtMetaData atMetaData = AtMetaData() + ..ttl = 10 + ..ttb = 10 + ..ttr = 10 + ..isCascade = false + ..isBinary = false + ..isEncrypted = false + ..dataSignature = 'dummy_data_signature_old' + ..sharedKeyEnc = 'dummy_shared_key_env_old' + ..pubKeyCS = 'dummy_public_key_cs_old' + ..encoding = 'base64_old' + ..encKeyName = 'dummy_enc_key_old' + ..encAlgo = 'rsa_old' + ..ivNonce = 'dummy_ivnonce_old' + ..skeEncKeyName = 'dummy_ske_old' + ..skeEncAlgo = 'dummy_ske_enc_algo_old'; + AtData atData = AtData() + ..data = value + ..metaData = atMetaData; + await hiveKeyStore?.put(key, atData); + + // Update the key with a new metadata + AtMetaData newAtMetaData = AtMetaData() + ..isBinary = true + ..isEncrypted = true + ..dataSignature = 'dummy_data_signature' + ..sharedKeyEnc = 'dummy_shared_key_env' + ..pubKeyCS = 'dummy_public_key_cs' + ..encoding = 'base64' + ..encKeyName = 'dummy_enc_key' + ..encAlgo = 'rsa' + ..ivNonce = 'dummy_ivnonce' + ..skeEncKeyName = 'dummy_ske' + ..skeEncAlgo = 'dummy_ske_enc_algo'; + + AtData newAtData = AtData() + ..data = value + ..metaData = newAtMetaData; + await hiveKeyStore?.put(key, newAtData); + + AtData? atDataResponse = await hiveKeyStore?.get(key); + expect(atDataResponse?.metaData?.ttl, 10); + expect(atDataResponse?.metaData?.ttb, 10); + expect(atDataResponse?.metaData?.ttr, 10); + expect(atDataResponse?.metaData?.isCascade, false); + expect(atDataResponse?.metaData?.isBinary, true); + expect(atDataResponse?.metaData?.isEncrypted, true); + expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); + expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); + expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); + expect(atDataResponse?.metaData?.encoding, 'base64'); + expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); + expect(atDataResponse?.metaData?.encAlgo, 'rsa'); + expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); + expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); + expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); + }); + + test('A test to verify null to new metadata reset the metadata', () async { + var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore(atSign)! + .getSecondaryKeyStore(); + + String key = '@bob:phone@alice'; + String value = '9878123321'; + AtMetaData atMetaData = AtMetaData() + ..ttl = 10 + ..ttb = 10 + ..ttr = 10 + ..isCascade = false + ..isBinary = true + ..isEncrypted = true + ..dataSignature = 'dummy_data_signature_old' + ..sharedKeyEnc = 'dummy_shared_key_env_old' + ..pubKeyCS = 'dummy_public_key_cs_old' + ..encoding = 'base64_old' + ..encKeyName = 'dummy_enc_key_old' + ..encAlgo = 'rsa_old' + ..ivNonce = 'dummy_ivnonce_old' + ..skeEncKeyName = 'dummy_ske_old' + ..skeEncAlgo = 'dummy_ske_enc_algo_old'; + AtData atData = AtData() + ..data = value + ..metaData = atMetaData; + await hiveKeyStore?.put(key, atData); + + // Update the key with a new metadata + AtMetaData newAtMetaData = AtMetaData() + ..dataSignature = 'null' + ..sharedKeyEnc = 'null' + ..pubKeyCS = 'null' + ..encoding = 'null' + ..encKeyName = 'null' + ..encAlgo = 'null' + ..ivNonce = 'null' + ..skeEncKeyName = 'null' + ..skeEncAlgo = 'null'; + + AtData newAtData = AtData() + ..data = value + ..metaData = newAtMetaData; + await hiveKeyStore?.put(key, newAtData); + + AtData? atDataResponse = await hiveKeyStore?.get(key); + expect(atDataResponse?.metaData?.ttl, 10); + expect(atDataResponse?.metaData?.ttb, 10); + expect(atDataResponse?.metaData?.ttr, 10); + expect(atDataResponse?.metaData?.isCascade, false); + expect(atDataResponse?.metaData?.isBinary, true); + expect(atDataResponse?.metaData?.isEncrypted, true); + expect(atDataResponse?.metaData?.dataSignature, null); + expect(atDataResponse?.metaData?.sharedKeyEnc, null); + expect(atDataResponse?.metaData?.pubKeyCS, null); + expect(atDataResponse?.metaData?.encoding, null); + expect(atDataResponse?.metaData?.encKeyName, null); + expect(atDataResponse?.metaData?.encAlgo, null); + expect(atDataResponse?.metaData?.ivNonce, null); + expect(atDataResponse?.metaData?.skeEncKeyName, null); + expect(atDataResponse?.metaData?.skeEncAlgo, null); + }); + + tearDownAll(() async => await tearDownFunc()); + }); } Future setUpFunc(storageDir, From 6c08e1da026e54171b2f5fd0950d4b5c0e93fe58 Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Wed, 5 Apr 2023 20:13:40 +0530 Subject: [PATCH 2/5] fix: Move logic that sets null or existing metadata to verbHandlers --- .../lib/src/model/at_metadata_builder.dart | 32 -- .../test/at_metadata_test.dart | 355 ++---------------- .../handler/abstract_update_verb_handler.dart | 43 ++- .../src/verb/handler/update_verb_handler.dart | 38 +- .../test/update_verb_test.dart | 271 +++++++------ 5 files changed, 252 insertions(+), 487 deletions(-) diff --git a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart index 8bc3faf09..eb68afe31 100644 --- a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart +++ b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart @@ -100,8 +100,6 @@ class AtMetadataBuilder { atMetaData.ivNonce = ivNonce; atMetaData.skeEncKeyName = skeEncKeyName; atMetaData.skeEncAlgo = skeEncAlgo; - - atMetaData = _setNullOrExistingMetadata(existingMetaData); } void setTTL(int? ttl, {int? ttb}) { @@ -160,36 +158,6 @@ class AtMetadataBuilder { return today.add(Duration(seconds: ttr)); } - /// If metadata contains "null" string, then reset the metadata. So set it to null - /// If metadata contains null (null object), then fetch the existing metadata.If - /// existing metadata value is not null, set it the current AtMetaData obj. - AtMetaData _setNullOrExistingMetadata(AtMetaData? existingAtMetadata) { - if (existingAtMetadata == null) { - return atMetaData; - } - var atMetaDataJson = atMetaData.toJson(); - var existingAtMetaDataJson = existingAtMetadata.toJson(); - atMetaDataJson.forEach((key, value) { - switch (value) { - // If command does not contains the attributes of a metadata, then regex named - // group, inserts null. For a key, if an attribute has a value in previously, - // fetch the value and update it. - case null: - if (existingAtMetaDataJson[key] != null) { - atMetaDataJson[key] = existingAtMetaDataJson[key]; - } - break; - // In the command, if an attribute is explicitly set to null, then verbParams - // contains String value "null". Then reset the metadata. So, set it to null - case 'null': - atMetaDataJson[key] = null; - break; - } - }); - - return AtMetaData.fromJson(atMetaDataJson); - } - AtMetaData build() { return atMetaData; } diff --git a/packages/at_persistence_secondary_server/test/at_metadata_test.dart b/packages/at_persistence_secondary_server/test/at_metadata_test.dart index f8a824c77..a226b8b82 100644 --- a/packages/at_persistence_secondary_server/test/at_metadata_test.dart +++ b/packages/at_persistence_secondary_server/test/at_metadata_test.dart @@ -119,8 +119,7 @@ void main() async { key, AtData()..data = '9878123322', AtMetaData()); // Update the same key var updateKeyDateTime = DateTime.now().toUtcMillisecondsPrecision(); - await hiveKeyStore?.putAll( - key, AtData()..data = '9878123322', AtMetaData()..ttl = 10000); + await hiveKeyStore?.putAll(key, AtData()..data = '9878123322', AtMetaData()..ttl = 10000); var atData = await hiveKeyStore?.get(key); expect(atData?.data, '9878123322'); expect( @@ -157,23 +156,18 @@ void main() async { group('Test json round-tripping', () { test('Test without null values', () { final Metadata startMetaData = Metadata() - ..ttl = 100 - ..ttb = 200 - ..ttr = 3600 - ..ccd = true - ..isBinary = false - ..isEncrypted = true - ..dataSignature = 'dataSignature' - ..pubKeyCS = 'pubKeyChecksum' - ..sharedKeyEnc = 'sharedKeyEncrypted' - ..encoding = 'someEncoding' - ..encKeyName = 'someEncKeyName' - ..encAlgo = 'AES/CTR/PKCS7Padding' - ..ivNonce = 'someIvNonce' - ..skeEncKeyName = 'someSkeEncKeyName' - ..skeEncAlgo = 'someSkeEncAlgo'; - final AtMetaData startAtMetaData = - AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl=100..ttb=200..ttr=3600 + ..ccd=true..isBinary=false..isEncrypted=true + ..dataSignature='dataSignature' + ..pubKeyCS='pubKeyChecksum' + ..sharedKeyEnc='sharedKeyEncrypted' + ..encoding='someEncoding' + ..encKeyName='someEncKeyName' + ..encAlgo='AES/CTR/PKCS7Padding' + ..ivNonce='someIvNonce' + ..skeEncKeyName='someSkeEncKeyName' + ..skeEncAlgo='someSkeEncAlgo'; + final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -183,25 +177,20 @@ void main() async { final Metadata endMetaData = endAtMetaData.toCommonsMetadata(); expect(endMetaData, startMetaData); }); - test('Test with null values', () { + test ('Test with null values', () { final Metadata startMetaData = Metadata() - ..ttl = null - ..ttb = null - ..ttr = null - ..ccd = false - ..isBinary = true - ..isEncrypted = false - ..dataSignature = null - ..pubKeyCS = null - ..sharedKeyEnc = null - ..encoding = null - ..encKeyName = null - ..encAlgo = null - ..ivNonce = null - ..skeEncKeyName = null - ..skeEncAlgo = null; - final AtMetaData startAtMetaData = - AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl=null..ttb=null..ttr=null + ..ccd=false..isBinary=true..isEncrypted=false + ..dataSignature=null + ..pubKeyCS=null + ..sharedKeyEnc=null + ..encoding=null + ..encKeyName=null + ..encAlgo=null + ..ivNonce=null + ..skeEncKeyName=null + ..skeEncAlgo=null; + final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -211,25 +200,20 @@ void main() async { final Metadata endMetaData = endAtMetaData.toCommonsMetadata(); expect(endMetaData, startMetaData); }); - test('Test with some null, some non-null values', () { + test ('Test with some null, some non-null values', () { final Metadata startMetaData = Metadata() - ..ttl = 0 - ..ttb = 0 - ..ttr = 0 - ..ccd = false - ..isBinary = true - ..isEncrypted = false - ..dataSignature = 'foo' - ..pubKeyCS = null - ..sharedKeyEnc = null - ..encoding = 'base64' - ..encKeyName = 'someEncKeyName' - ..encAlgo = 'AES/CTR/PKCS7Padding' - ..ivNonce = 'someIvOrNonce' - ..skeEncKeyName = null - ..skeEncAlgo = null; - final AtMetaData startAtMetaData = - AtMetaData.fromCommonsMetadata(startMetaData); + ..ttl=0..ttb=0..ttr=0 + ..ccd=false..isBinary=true..isEncrypted=false + ..dataSignature='foo' + ..pubKeyCS=null + ..sharedKeyEnc=null + ..encoding='base64' + ..encKeyName='someEncKeyName' + ..encAlgo='AES/CTR/PKCS7Padding' + ..ivNonce='someIvOrNonce' + ..skeEncKeyName=null + ..skeEncAlgo=null; + final AtMetaData startAtMetaData = AtMetaData.fromCommonsMetadata(startMetaData); final Map startMap = startAtMetaData.toJson(); final String startJson = jsonEncode(startMap); final Map endMap = jsonDecode(startJson); @@ -240,267 +224,6 @@ void main() async { expect(endMetaData, startMetaData); }); }); - - group('A group of tests to assert metadata when key is updated', () { - setUpAll(() async => await setUpFunc(storageDir)); - - test( - 'A test to verify existing metadata is retained when key is updated using put method', - () async { - var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore(atSign)! - .getSecondaryKeyStore(); - String key = '@bob:phone@alice'; - String value = '9878123321'; - AtMetaData atMetaData = AtMetaData() - ..ttl = 100 - ..ttb = 100 - ..ttr = 1000 - ..isCascade = true - ..isBinary = true - ..isEncrypted = true - ..dataSignature = 'dummy_data_signature' - ..sharedKeyEnc = 'dummy_shared_key_env' - ..pubKeyCS = 'dummy_public_key_cs' - ..encoding = 'base64' - ..encKeyName = 'dummy_enc_key' - ..encAlgo = 'rsa' - ..ivNonce = 'dummy_ivnonce' - ..skeEncKeyName = 'dummy_ske' - ..skeEncAlgo = 'dummy_ske_enc_algo'; - - AtData atData = AtData() - ..data = value - ..metaData = atMetaData; - await hiveKeyStore?.put(key, atData); - // Update the key with a no metadata - AtMetaData newAtMetaData = AtMetaData(); - AtData newAtData = AtData() - ..data = value - ..metaData = newAtMetaData; - await hiveKeyStore?.put(key, newAtData); - - AtData? atDataResponse = await hiveKeyStore?.get(key); - expect(atDataResponse?.metaData?.ttl, 100); - expect(atDataResponse?.metaData?.ttb, 100); - expect(atDataResponse?.metaData?.ttr, 1000); - expect(atDataResponse?.metaData?.isCascade, true); - expect(atDataResponse?.metaData?.isBinary, true); - expect(atDataResponse?.metaData?.isEncrypted, true); - expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); - expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); - expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); - expect(atDataResponse?.metaData?.encoding, 'base64'); - expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); - expect(atDataResponse?.metaData?.encAlgo, 'rsa'); - expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); - expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); - expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); - }); - - test( - 'A test to verify new metadata overwrites the existing metadata using put method', - () async { - var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore(atSign)! - .getSecondaryKeyStore(); - - String key = '@bob:phone@alice'; - String value = '9878123321'; - AtMetaData atMetaData = AtMetaData() - ..ttl = 10 - ..ttb = 10 - ..ttr = 10 - ..isCascade = false - ..isBinary = false - ..isEncrypted = false - ..dataSignature = 'dummy_data_signature_old' - ..sharedKeyEnc = 'dummy_shared_key_env_old' - ..pubKeyCS = 'dummy_public_key_cs_old' - ..encoding = 'base64_old' - ..encKeyName = 'dummy_enc_key_old' - ..encAlgo = 'rsa_old' - ..ivNonce = 'dummy_ivnonce_old' - ..skeEncKeyName = 'dummy_ske_old' - ..skeEncAlgo = 'dummy_ske_enc_algo_old'; - AtData atData = AtData() - ..data = value - ..metaData = atMetaData; - await hiveKeyStore?.put(key, atData); - - // Update the key with a new metadata - AtMetaData newAtMetaData = AtMetaData() - ..ttl = 100 - ..ttb = 100 - ..ttr = 1000 - ..isCascade = true - ..isBinary = true - ..isEncrypted = true - ..dataSignature = 'dummy_data_signature' - ..sharedKeyEnc = 'dummy_shared_key_env' - ..pubKeyCS = 'dummy_public_key_cs' - ..encoding = 'base64' - ..encKeyName = 'dummy_enc_key' - ..encAlgo = 'rsa' - ..ivNonce = 'dummy_ivnonce' - ..skeEncKeyName = 'dummy_ske' - ..skeEncAlgo = 'dummy_ske_enc_algo'; - - AtData newAtData = AtData() - ..data = value - ..metaData = newAtMetaData; - await hiveKeyStore?.put(key, newAtData); - - AtData? atDataResponse = await hiveKeyStore?.get(key); - expect(atDataResponse?.metaData?.ttl, 100); - expect(atDataResponse?.metaData?.ttb, 100); - expect(atDataResponse?.metaData?.ttr, 1000); - expect(atDataResponse?.metaData?.isCascade, true); - expect(atDataResponse?.metaData?.isBinary, true); - expect(atDataResponse?.metaData?.isEncrypted, true); - expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); - expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); - expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); - expect(atDataResponse?.metaData?.encoding, 'base64'); - expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); - expect(atDataResponse?.metaData?.encAlgo, 'rsa'); - expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); - expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); - expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); - }); - - test( - 'A test to verify new metadata is applied for fields that are modified and existing metadata is retained for the fields that are not updated', - () async { - var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore(atSign)! - .getSecondaryKeyStore(); - - String key = '@bob:phone@alice'; - String value = '9878123321'; - AtMetaData atMetaData = AtMetaData() - ..ttl = 10 - ..ttb = 10 - ..ttr = 10 - ..isCascade = false - ..isBinary = false - ..isEncrypted = false - ..dataSignature = 'dummy_data_signature_old' - ..sharedKeyEnc = 'dummy_shared_key_env_old' - ..pubKeyCS = 'dummy_public_key_cs_old' - ..encoding = 'base64_old' - ..encKeyName = 'dummy_enc_key_old' - ..encAlgo = 'rsa_old' - ..ivNonce = 'dummy_ivnonce_old' - ..skeEncKeyName = 'dummy_ske_old' - ..skeEncAlgo = 'dummy_ske_enc_algo_old'; - AtData atData = AtData() - ..data = value - ..metaData = atMetaData; - await hiveKeyStore?.put(key, atData); - - // Update the key with a new metadata - AtMetaData newAtMetaData = AtMetaData() - ..isBinary = true - ..isEncrypted = true - ..dataSignature = 'dummy_data_signature' - ..sharedKeyEnc = 'dummy_shared_key_env' - ..pubKeyCS = 'dummy_public_key_cs' - ..encoding = 'base64' - ..encKeyName = 'dummy_enc_key' - ..encAlgo = 'rsa' - ..ivNonce = 'dummy_ivnonce' - ..skeEncKeyName = 'dummy_ske' - ..skeEncAlgo = 'dummy_ske_enc_algo'; - - AtData newAtData = AtData() - ..data = value - ..metaData = newAtMetaData; - await hiveKeyStore?.put(key, newAtData); - - AtData? atDataResponse = await hiveKeyStore?.get(key); - expect(atDataResponse?.metaData?.ttl, 10); - expect(atDataResponse?.metaData?.ttb, 10); - expect(atDataResponse?.metaData?.ttr, 10); - expect(atDataResponse?.metaData?.isCascade, false); - expect(atDataResponse?.metaData?.isBinary, true); - expect(atDataResponse?.metaData?.isEncrypted, true); - expect(atDataResponse?.metaData?.dataSignature, 'dummy_data_signature'); - expect(atDataResponse?.metaData?.sharedKeyEnc, 'dummy_shared_key_env'); - expect(atDataResponse?.metaData?.pubKeyCS, 'dummy_public_key_cs'); - expect(atDataResponse?.metaData?.encoding, 'base64'); - expect(atDataResponse?.metaData?.encKeyName, 'dummy_enc_key'); - expect(atDataResponse?.metaData?.encAlgo, 'rsa'); - expect(atDataResponse?.metaData?.ivNonce, 'dummy_ivnonce'); - expect(atDataResponse?.metaData?.skeEncKeyName, 'dummy_ske'); - expect(atDataResponse?.metaData?.skeEncAlgo, 'dummy_ske_enc_algo'); - }); - - test('A test to verify null to new metadata reset the metadata', () async { - var hiveKeyStore = SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore(atSign)! - .getSecondaryKeyStore(); - - String key = '@bob:phone@alice'; - String value = '9878123321'; - AtMetaData atMetaData = AtMetaData() - ..ttl = 10 - ..ttb = 10 - ..ttr = 10 - ..isCascade = false - ..isBinary = true - ..isEncrypted = true - ..dataSignature = 'dummy_data_signature_old' - ..sharedKeyEnc = 'dummy_shared_key_env_old' - ..pubKeyCS = 'dummy_public_key_cs_old' - ..encoding = 'base64_old' - ..encKeyName = 'dummy_enc_key_old' - ..encAlgo = 'rsa_old' - ..ivNonce = 'dummy_ivnonce_old' - ..skeEncKeyName = 'dummy_ske_old' - ..skeEncAlgo = 'dummy_ske_enc_algo_old'; - AtData atData = AtData() - ..data = value - ..metaData = atMetaData; - await hiveKeyStore?.put(key, atData); - - // Update the key with a new metadata - AtMetaData newAtMetaData = AtMetaData() - ..dataSignature = 'null' - ..sharedKeyEnc = 'null' - ..pubKeyCS = 'null' - ..encoding = 'null' - ..encKeyName = 'null' - ..encAlgo = 'null' - ..ivNonce = 'null' - ..skeEncKeyName = 'null' - ..skeEncAlgo = 'null'; - - AtData newAtData = AtData() - ..data = value - ..metaData = newAtMetaData; - await hiveKeyStore?.put(key, newAtData); - - AtData? atDataResponse = await hiveKeyStore?.get(key); - expect(atDataResponse?.metaData?.ttl, 10); - expect(atDataResponse?.metaData?.ttb, 10); - expect(atDataResponse?.metaData?.ttr, 10); - expect(atDataResponse?.metaData?.isCascade, false); - expect(atDataResponse?.metaData?.isBinary, true); - expect(atDataResponse?.metaData?.isEncrypted, true); - expect(atDataResponse?.metaData?.dataSignature, null); - expect(atDataResponse?.metaData?.sharedKeyEnc, null); - expect(atDataResponse?.metaData?.pubKeyCS, null); - expect(atDataResponse?.metaData?.encoding, null); - expect(atDataResponse?.metaData?.encKeyName, null); - expect(atDataResponse?.metaData?.encAlgo, null); - expect(atDataResponse?.metaData?.ivNonce, null); - expect(atDataResponse?.metaData?.skeEncKeyName, null); - expect(atDataResponse?.metaData?.skeEncAlgo, null); - }); - - tearDownAll(() async => await tearDownFunc()); - }); } Future setUpFunc(storageDir, diff --git a/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart b/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart index d9585b57c..36122e439 100644 --- a/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart +++ b/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart @@ -109,6 +109,9 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { atData.metaData = AtMetaData.fromCommonsMetadata(updateParams.metadata!); + atData.metaData = + _setNullOrExistingMetadata(atData.metaData!, existingAtMetaData); + notify( sharedBy, sharedWith, @@ -117,7 +120,7 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { SecondaryUtil.getNotificationPriority(verbParams[PRIORITY]), atData.metaData!); - return UpdatePreProcessResult(atKey, atData, updateParams); + return UpdatePreProcessResult(atKey, atData); } UpdateParams getUpdateParams(HashMap verbParams) { @@ -179,8 +182,9 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { return; } key = '$forAtSign:$key$atSign'; - int ttlInMillis = Duration(minutes: AtSecondaryConfig.notificationExpiryInMins) - .inMilliseconds; + int ttlInMillis = + Duration(minutes: AtSecondaryConfig.notificationExpiryInMins) + .inMilliseconds; var atNotification = (AtNotificationBuilder() ..fromAtSign = atSign @@ -197,12 +201,41 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { unawaited(notificationManager.notify(atNotification)); return atNotification; } + + /// If metadata contains "null" string, then reset the metadata. So set it to null + /// If metadata contains null (null object), then fetch the existing metadata.If + /// existing metadata value is not null, set it the current AtMetaData obj. + AtMetaData _setNullOrExistingMetadata( + AtMetaData newAtMetadata, AtMetaData? existingAtMetadata) { + if (existingAtMetadata == null) { + return newAtMetadata; + } + var atMetaDataJson = newAtMetadata.toJson(); + var existingAtMetaDataJson = existingAtMetadata.toJson(); + atMetaDataJson.forEach((key, value) { + switch (value) { + // If command does not contains the attributes of a metadata, then regex named + // group, inserts null. For a key, if an attribute has a value in previously, + // fetch the value and update it. + case null: + if (existingAtMetaDataJson[key] != null) { + atMetaDataJson[key] = existingAtMetaDataJson[key]; + } + break; + // In the command, if an attribute is explicitly set to null, then verbParams + // contains String value "null". Then reset the metadata. So, set it to null + case 'null': + atMetaDataJson[key] = null; + break; + } + }); + return AtMetaData.fromJson(atMetaDataJson); + } } class UpdatePreProcessResult { String atKey; AtData atData; - UpdateParams updateParams; - UpdatePreProcessResult(this.atKey, this.atData, this.updateParams); + UpdatePreProcessResult(this.atKey, this.atData); } diff --git a/packages/at_secondary_server/lib/src/verb/handler/update_verb_handler.dart b/packages/at_secondary_server/lib/src/verb/handler/update_verb_handler.dart index 04d6ca837..f2997c7e2 100644 --- a/packages/at_secondary_server/lib/src/verb/handler/update_verb_handler.dart +++ b/packages/at_secondary_server/lib/src/verb/handler/update_verb_handler.dart @@ -13,8 +13,8 @@ import 'package:at_server_spec/at_verb_spec.dart'; class UpdateVerbHandler extends AbstractUpdateVerbHandler { static Update update = Update(); - UpdateVerbHandler( - SecondaryKeyStore keyStore, StatsNotificationService statsNotificationService, notificationManager) + UpdateVerbHandler(SecondaryKeyStore keyStore, + StatsNotificationService statsNotificationService, notificationManager) : super(keyStore, statsNotificationService, notificationManager); // Method to verify whether command is accepted or not @@ -37,12 +37,9 @@ class UpdateVerbHandler extends AbstractUpdateVerbHandler { Response response, HashMap verbParams, InboundConnection atConnection) async { - var updatePreProcessResult = await super.preProcessAndNotify(response, verbParams, atConnection); - var updateParams = updatePreProcessResult.updateParams; - logger.finer( 'calling keyStore.put(${updatePreProcessResult.atKey}, ${updatePreProcessResult.atData}'); @@ -50,21 +47,22 @@ class UpdateVerbHandler extends AbstractUpdateVerbHandler { // update the key in data store var result = await keyStore.put( updatePreProcessResult.atKey, updatePreProcessResult.atData, - time_to_live: updateParams.metadata!.ttl, - time_to_born: updateParams.metadata!.ttb, - time_to_refresh: updateParams.metadata!.ttr, - isCascade: updateParams.metadata!.ccd, - isBinary: updateParams.metadata!.isBinary, - isEncrypted: updateParams.metadata!.isEncrypted, - dataSignature: updateParams.metadata!.dataSignature, - sharedKeyEncrypted: updateParams.metadata!.sharedKeyEnc, - publicKeyChecksum: updateParams.metadata!.pubKeyCS, - encoding: updateParams.metadata!.encoding, - encKeyName: updateParams.metadata!.encKeyName, - encAlgo: updateParams.metadata!.encAlgo, - ivNonce: updateParams.metadata!.ivNonce, - skeEncKeyName: updateParams.metadata!.skeEncKeyName, - skeEncAlgo: updateParams.metadata!.skeEncAlgo); + time_to_live: updatePreProcessResult.atData.metaData!.ttl, + time_to_born: updatePreProcessResult.atData.metaData!.ttb, + time_to_refresh: updatePreProcessResult.atData.metaData!.ttr, + isCascade: updatePreProcessResult.atData.metaData!.isCascade, + isBinary: updatePreProcessResult.atData.metaData!.isBinary, + isEncrypted: updatePreProcessResult.atData.metaData!.isEncrypted, + dataSignature: updatePreProcessResult.atData.metaData!.dataSignature, + sharedKeyEncrypted: + updatePreProcessResult.atData.metaData!.sharedKeyEnc, + publicKeyChecksum: updatePreProcessResult.atData.metaData!.pubKeyCS, + encoding: updatePreProcessResult.atData.metaData!.encoding, + encKeyName: updatePreProcessResult.atData.metaData!.encKeyName, + encAlgo: updatePreProcessResult.atData.metaData!.encAlgo, + ivNonce: updatePreProcessResult.atData.metaData!.ivNonce, + skeEncKeyName: updatePreProcessResult.atData.metaData!.skeEncKeyName, + skeEncAlgo: updatePreProcessResult.atData.metaData!.skeEncAlgo); response.data = result?.toString(); } catch (e, st) { logger.warning('$e\n$st'); diff --git a/packages/at_secondary_server/test/update_verb_test.dart b/packages/at_secondary_server/test/update_verb_test.dart index 8677f86f2..47668929b 100644 --- a/packages/at_secondary_server/test/update_verb_test.dart +++ b/packages/at_secondary_server/test/update_verb_test.dart @@ -43,14 +43,16 @@ void main() { group('A group of update accept tests', () { test('test update command accept test', () { var command = 'update:public:location@alice new york'; - var handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + var handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); var result = handler.accept(command); expect(result, true); }); test('test update command accept negative test', () { var command = 'updated:public:location@alice new york'; - var handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + var handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); var result = handler.accept(command); expect(result, false); }); @@ -135,8 +137,10 @@ void main() { var verb = Update(); var command = 'update:ttl:1:public:location:city@alice Hyderabad:TG'; var regex = verb.syntax(); - expect(() => getVerbParam(regex, command), - throwsA(predicate((dynamic e) => e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + expect( + () => getVerbParam(regex, command), + throwsA(predicate((dynamic e) => + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update key- no atsign', () { @@ -151,15 +155,18 @@ void main() { var verb = Update(); var command = 'update:location:local us'; var regex = verb.syntax(); - expect(() => getVerbParam(regex, command), - throwsA(predicate((dynamic e) => e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + expect( + () => getVerbParam(regex, command), + throwsA(predicate((dynamic e) => + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); }); group('A group of update verb handler test', () { test('test update verb handler- update', () { var command = 'update:location@alice us'; - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); var verbParameters = handler.parse(command); var verb = handler.getVerb(); expect(verb is Update, true); @@ -172,7 +179,8 @@ void main() { test('test update verb handler- public update', () { var command = 'update:public:location@alice us'; - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); var verb = handler.getVerb(); var verbParameters = handler.parse(command); @@ -216,9 +224,9 @@ void main() { var command = 'update:ttl::public:location:city@alice Hyderabad:TG'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update with ttb with no value', () { @@ -226,9 +234,9 @@ void main() { var command = 'update:ttb::public:location:city@alice Hyderabad:TG'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update with two colons beside - invalid syntax', () { @@ -236,9 +244,9 @@ void main() { var command = 'update::location:city@alice Hyderabad:TG'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update with @ suffixed in atsign - invalid syntax', () { @@ -246,9 +254,9 @@ void main() { var command = 'update:location:city@alice@ Hyderabad:TG'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update key- no value', () { @@ -256,9 +264,9 @@ void main() { var command = 'update:location@alice '; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update key- invalid keyword', () { @@ -266,9 +274,9 @@ void main() { var command = 'updatee:location@alice us'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update verb - no key', () { @@ -276,9 +284,9 @@ void main() { var command = 'update: us'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update verb - with public and private for atSign', () { @@ -286,18 +294,19 @@ void main() { var command = 'update:public:@kevin:location@bob us'; var regex = verb.syntax(); expect( - () => getVerbParam(regex, command), + () => getVerbParam(regex, command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && e.message == 'Syntax Exception'))); + e is InvalidSyntaxException && e.message == 'Syntax Exception'))); }); test('test update key no value - invalid command', () { var command = 'update:location@alice'; - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); expect( - () => handler.parse(command), + () => handler.parse(command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && + e is InvalidSyntaxException && e.message == 'Invalid syntax. ${handler.getVerb().usage()}'))); }); }); @@ -389,13 +398,14 @@ void main() { command = SecondaryUtil.convertCommand(command); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; var secondaryPersistenceStore = - SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore( - AtSecondaryServerImpl.getInstance().currentAtSign)!; + SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore( + AtSecondaryServerImpl.getInstance().currentAtSign)!; SecondaryKeyStore keyStore = secondaryPersistenceStore .getSecondaryKeyStoreManager()! .getKeyStore(); - AbstractVerbHandler handler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); Map parsed = handler.parse(command); expect(parsed['ttl'], '-1'); }); @@ -405,60 +415,63 @@ void main() { command = SecondaryUtil.convertCommand(command); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; var secondaryPersistenceStore = - SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore( - AtSecondaryServerImpl.getInstance().currentAtSign)!; + SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore( + AtSecondaryServerImpl.getInstance().currentAtSign)!; SecondaryKeyStore keyStore = secondaryPersistenceStore .getSecondaryKeyStoreManager()! .getKeyStore(); - AbstractVerbHandler handler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); Map parsed = handler.parse(command); expect(parsed['ttb'], '-1'); }); test('ttl and ttb starting with negative value -1', () { - var command = 'update:ttl:-1:ttb:-1:@bob:location.test@alice Hyderabad,TG'; + var command = + 'update:ttl:-1:ttb:-1:@bob:location.test@alice Hyderabad,TG'; command = SecondaryUtil.convertCommand(command); - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); Map parsed = handler.parse(command); - expect (parsed['ttl'], '-1'); - expect (parsed['ttb'], '-1'); + expect(parsed['ttl'], '-1'); + expect(parsed['ttb'], '-1'); }); test('ttl with no value - invalid syntax', () { var command = 'UpDaTe:ttl::@bob:location@alice Hyderabad,TG'; command = SecondaryUtil.convertCommand(command); - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); expect( - () => handler.parse(command), + () => handler.parse(command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && - e.message == - 'Invalid syntax. ${handler.getVerb().usage()}'))); + e is InvalidSyntaxException && + e.message == 'Invalid syntax. ${handler.getVerb().usage()}'))); }); test('ttb with no value - invalid syntax', () { var command = 'UpDaTe:ttb::@bob:location@alice Hyderabad,TG'; command = SecondaryUtil.convertCommand(command); - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); expect( - () => handler.parse(command), + () => handler.parse(command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && - e.message == - 'Invalid syntax. ${handler.getVerb().usage()}'))); + e is InvalidSyntaxException && + e.message == 'Invalid syntax. ${handler.getVerb().usage()}'))); }); test('ttl and ttb with no value - invalid syntax', () { var command = 'UpDaTe:ttl::ttb::@bob:location@alice Hyderabad,TG'; command = SecondaryUtil.convertCommand(command); - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); expect( - () => handler.parse(command), + () => handler.parse(command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && - e.message == - 'Invalid syntax. ${handler.getVerb().usage()}'))); + e is InvalidSyntaxException && + e.message == 'Invalid syntax. ${handler.getVerb().usage()}'))); }); }); @@ -498,20 +511,21 @@ void main() { command = SecondaryUtil.convertCommand(command); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; var secondaryPersistenceStore = - SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore( - AtSecondaryServerImpl.getInstance().currentAtSign)!; + SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore( + AtSecondaryServerImpl.getInstance().currentAtSign)!; SecondaryKeyStore keyStore = secondaryPersistenceStore .getSecondaryKeyStoreManager()! .getKeyStore(); - AbstractVerbHandler handler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); var response = Response(); var verbParams = handler.parse(command); var atConnection = InboundConnectionImpl(null, null); expect( - () => handler.processVerb(response, verbParams, atConnection), + () => handler.processVerb(response, verbParams, atConnection), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && + e is InvalidSyntaxException && e.message == 'Valid values for TTR are -1 and greater than or equal to 1'))); }); @@ -519,13 +533,13 @@ void main() { test('ccd with invalid value', () { var command = 'UpDaTe:ttr:1000:ccd:test:@bob:location@alice Hyderabad,TG'; command = SecondaryUtil.convertCommand(command); - AbstractVerbHandler handler = UpdateVerbHandler(mockKeyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + mockKeyStore, statsNotificationService, notificationManager); expect( - () => handler.parse(command), + () => handler.parse(command), throwsA(predicate((dynamic e) => - e is InvalidSyntaxException && - e.message == - 'Invalid syntax. ${handler.getVerb().usage()}'))); + e is InvalidSyntaxException && + e.message == 'Invalid syntax. ${handler.getVerb().usage()}'))); }); }); @@ -533,7 +547,7 @@ void main() { test('test update processVerb with local key', () async { var secretData = AtData(); secretData.data = - 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; + 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; await secondaryKeyStore.put('privatekey:at_secret', secretData); var fromVerbHandler = FromVerbHandler(secondaryKeyStore); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; @@ -554,11 +568,12 @@ void main() { await cramVerbHandler.processVerb( cramResponse, cramVerbParams, atConnection); var connectionMetadata = - atConnection.getMetaData() as InboundConnectionMetadata; + atConnection.getMetaData() as InboundConnectionMetadata; expect(connectionMetadata.isAuthenticated, true); expect(cramResponse.data, 'success'); //Update Verb - var updateVerbHandler = UpdateVerbHandler(secondaryKeyStore, statsNotificationService, notificationManager); + var updateVerbHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); var updateResponse = Response(); var updateVerbParams = HashMap(); updateVerbParams.putIfAbsent('atSign', () => '@alice'); @@ -579,7 +594,7 @@ void main() { test('test update processVerb with ttl and ttb', () async { var secretData = AtData(); secretData.data = - 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; + 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; await secondaryKeyStore.put('privatekey:at_secret', secretData); var fromVerbHandler = FromVerbHandler(secondaryKeyStore); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; @@ -600,11 +615,12 @@ void main() { await cramVerbHandler.processVerb( cramResponse, cramVerbParams, atConnection); var connectionMetadata = - atConnection.getMetaData() as InboundConnectionMetadata; + atConnection.getMetaData() as InboundConnectionMetadata; expect(connectionMetadata.isAuthenticated, true); expect(cramResponse.data, 'success'); //Update Verb - var updateVerbHandler = UpdateVerbHandler(secondaryKeyStore, statsNotificationService, notificationManager); + var updateVerbHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); var updateResponse = Response(); var updateVerbParams = HashMap(); @@ -655,7 +671,7 @@ void main() { test('Test to verify reset of TTB', () async { var secretData = AtData(); secretData.data = - 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; + 'b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364'; await secondaryKeyStore.put('privatekey:at_secret', secretData); var fromVerbHandler = FromVerbHandler(secondaryKeyStore); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; @@ -676,11 +692,12 @@ void main() { await cramVerbHandler.processVerb( cramResponse, cramVerbParams, atConnection); var connectionMetadata = - atConnection.getMetaData() as InboundConnectionMetadata; + atConnection.getMetaData() as InboundConnectionMetadata; expect(connectionMetadata.isAuthenticated, true); expect(cramResponse.data, 'success'); //Update Verb - var updateVerbHandler = UpdateVerbHandler(secondaryKeyStore, statsNotificationService, notificationManager); + var updateVerbHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); var updateResponse = Response(); var updateVerbParams = HashMap(); updateVerbParams.putIfAbsent(AT_SIGN, () => '@alice'); @@ -715,11 +732,12 @@ void main() { }); test('test auto_notify notification expiry', () async { - SecondaryKeyStore keyStore = secondaryPersistenceStore - !.getSecondaryKeyStoreManager()! + SecondaryKeyStore keyStore = secondaryPersistenceStore! + .getSecondaryKeyStoreManager()! .getKeyStore(); AbstractUpdateVerbHandler.setAutoNotify(true); - UpdateVerbHandler updateHandler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + UpdateVerbHandler updateHandler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); AtMetaData metaData = AtMetaData()..ttl = 1000; AtNotification? autoNotification; @@ -769,72 +787,95 @@ void main() { expect( updateCommand, 'update' - ':sharedKeyEnc:$ske' - ':pubKeyCS:$pubKeyCS' - ':encKeyName:some_key' - ':encAlgo:some_algo' - ':ivNonce:some_iv' - ':skeEncKeyName:$skeEncKeyName' - ':skeEncAlgo:$skeEncAlgo' - ':$bob:$atKey$alice $value'); + ':sharedKeyEnc:$ske' + ':pubKeyCS:$pubKeyCS' + ':encKeyName:some_key' + ':encAlgo:some_algo' + ':ivNonce:some_iv' + ':skeEncKeyName:$skeEncKeyName' + ':skeEncAlgo:$skeEncAlgo' + ':$bob:$atKey$alice $value'); inboundConnection.metadata.isAuthenticated = true; // 1. do an update and verify via llookup - UpdateVerbHandler updateHandler = UpdateVerbHandler(secondaryKeyStore, statsNotificationService, notificationManager); + UpdateVerbHandler updateHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); await updateHandler.process(updateCommand, inboundConnection); - LocalLookupVerbHandler llookupHandler = LocalLookupVerbHandler(secondaryKeyStore); - await llookupHandler.process('llookup:all:$bob:$atKey$alice', inboundConnection); + LocalLookupVerbHandler llookupHandler = + LocalLookupVerbHandler(secondaryKeyStore); + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); Map mapSentToClient = decodeResponse(inboundConnection.lastWrittenData!); expect(mapSentToClient['key'], '$bob:$atKey$alice'); expect(mapSentToClient['data'], value); - expect(AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(), + expect( + AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(), updateBuilder.metadata); // 2. update just the value and verify updateBuilder.value = value = 'alice@wowzer.net'; - await updateHandler.process(updateBuilder.buildCommand().trim(), inboundConnection); - await llookupHandler.process('llookup:all:$bob:$atKey$alice', inboundConnection); + await updateHandler.process( + updateBuilder.buildCommand().trim(), inboundConnection); + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); mapSentToClient = decodeResponse(inboundConnection.lastWrittenData!); expect(mapSentToClient['key'], '$bob:$atKey$alice'); expect(mapSentToClient['data'], value); - expect(AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(), + expect( + AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(), updateBuilder.metadata); // 3. update just some of the metadata and verify - updateBuilder.skeEncKeyName = null; - updateBuilder.skeEncAlgo = null; - updateBuilder.sharedKeyEncrypted = null; + // Setting few metadata to 'null' to reset them + updateBuilder.skeEncKeyName = 'null'; + updateBuilder.skeEncAlgo = 'null'; + updateBuilder.sharedKeyEncrypted = 'null'; updateBuilder.encAlgo = 'WOW/MUCH/ENCRYPTION'; updateBuilder.encKeyName = 'such_secret_key'; - await updateHandler.process(updateBuilder.buildCommand().trim(), inboundConnection); - await llookupHandler.process('llookup:all:$bob:$atKey$alice', inboundConnection); + updateBuilder.dataSignature = 'data_signature_to_validate_public_data'; + await updateHandler.process( + updateBuilder.buildCommand().trim(), inboundConnection); + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); mapSentToClient = decodeResponse(inboundConnection.lastWrittenData!); expect(mapSentToClient['key'], '$bob:$atKey$alice'); expect(mapSentToClient['data'], value); - var receivedMetadata = AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(); + var receivedMetadata = + AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(); expect(receivedMetadata.encAlgo, 'WOW/MUCH/ENCRYPTION'); expect(receivedMetadata.encKeyName, 'such_secret_key'); - expect(receivedMetadata, updateBuilder.metadata); + // When attributes are set to String null, the metadata is reset. + expect(receivedMetadata.sharedKeyEnc, null); + expect(receivedMetadata.skeEncAlgo, null); + expect(receivedMetadata.skeEncKeyName, null); // 4. let's update the value and a load of random metadata, and verify updateBuilder.atKeyObj.metadata = createRandomCommonsMetadata(); + // Setting ttb to null to test existing value is fetched and updated. updateBuilder.ttb = null; + updateBuilder.dataSignature = null; updateBuilder.ttr = 10; updateBuilder.value = value = 'alice@wonder.land'; - await updateHandler.process(updateBuilder.buildCommand().trim(), inboundConnection); - await llookupHandler.process('llookup:all:$bob:$atKey$alice', inboundConnection); + await updateHandler.process( + updateBuilder.buildCommand().trim(), inboundConnection); + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); var sentToClient = inboundConnection.lastWrittenData!; mapSentToClient = decodeResponse(sentToClient); expect(mapSentToClient['key'], '$bob:$atKey$alice'); expect(mapSentToClient['data'], value); - expect(AtMetaData.fromJson(mapSentToClient['metaData']).toCommonsMetadata(), - updateBuilder.metadata); + var atMetadata = AtMetaData.fromJson(mapSentToClient['metaData']); + expect( + atMetadata.dataSignature, 'data_signature_to_validate_public_data'); + expect(atMetadata.ttr, 10); + expect(atMetadata.ttb, null); await secondaryKeyStore.remove('$bob:$atKey$alice'); } + test('update with all metadata', () async { for (int i = 0; i < 100; i++) { await doit(); @@ -848,21 +889,22 @@ void main() { command = SecondaryUtil.convertCommand(command); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; var secondaryPersistenceStore = - SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore( - AtSecondaryServerImpl.getInstance().currentAtSign)!; + SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore( + AtSecondaryServerImpl.getInstance().currentAtSign)!; SecondaryKeyStore keyStore = secondaryPersistenceStore .getSecondaryKeyStoreManager()! .getKeyStore(); - AbstractVerbHandler handler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); var response = Response(); var verbParams = handler.parse(command); var atConnection = InboundConnectionImpl(null, null); await expectLater( - () async => - await handler.processVerb(response, verbParams, atConnection), + () async => + await handler.processVerb(response, verbParams, atConnection), throwsA(predicate((dynamic e) => - e is InvalidAtKeyException && + e is InvalidAtKeyException && e.message == 'Invalid update command - sharedBy atsign @bob should be same as current atsign @alice'))); }); @@ -871,13 +913,14 @@ void main() { command = SecondaryUtil.convertCommand(command); AtSecondaryServerImpl.getInstance().currentAtSign = '@alice'; var secondaryPersistenceStore = - SecondaryPersistenceStoreFactory.getInstance() - .getSecondaryPersistenceStore( - AtSecondaryServerImpl.getInstance().currentAtSign)!; + SecondaryPersistenceStoreFactory.getInstance() + .getSecondaryPersistenceStore( + AtSecondaryServerImpl.getInstance().currentAtSign)!; SecondaryKeyStore keyStore = secondaryPersistenceStore .getSecondaryKeyStoreManager()! .getKeyStore(); - AbstractVerbHandler handler = UpdateVerbHandler(keyStore, statsNotificationService, notificationManager); + AbstractVerbHandler handler = UpdateVerbHandler( + keyStore, statsNotificationService, notificationManager); var response = Response(); var verbParams = handler.parse(command); var atConnection = InboundConnectionImpl(null, null); From efdaedeaba0c96be327e7990bb281d414998866f Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Thu, 6 Apr 2023 10:39:14 +0530 Subject: [PATCH 3/5] fix: Revert version and CHANGELOG.md in at_persistence_secondary_server --- .../CHANGELOG.md | 2 - .../lib/src/model/at_metadata_builder.dart | 69 ++++++++++--------- .../pubspec.yaml | 2 +- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/packages/at_persistence_secondary_server/CHANGELOG.md b/packages/at_persistence_secondary_server/CHANGELOG.md index f6ab47d40..0d7597c68 100644 --- a/packages/at_persistence_secondary_server/CHANGELOG.md +++ b/packages/at_persistence_secondary_server/CHANGELOG.md @@ -1,5 +1,3 @@ -## 3.0.53 -- fix: Modify the AtMetadataBuilder to preserve the existing metadata when not set. Reset the metadata when explicitly set to null. ## 3.0.52 - feat: Add new encryption metadata fields to core persistence classes ## 3.0.51 diff --git a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart index eb68afe31..43ec85814 100644 --- a/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart +++ b/packages/at_persistence_secondary_server/lib/src/model/at_metadata_builder.dart @@ -4,12 +4,10 @@ import 'package:at_utils/at_logger.dart'; /// Builder class to build [AtMetaData] object. class AtMetadataBuilder { late AtMetaData atMetaData; - /// We will constrain to millisecond precision because Hive only stores /// [DateTime]s to millisecond precision - see https://github.com/hivedb/hive/issues/474 /// for details. - var currentUtcTimeToMillisecondPrecision = - DateTime.now().toUtcMillisecondsPrecision(); + var currentUtcTimeToMillisecondPrecision = DateTime.now().toUtcMillisecondsPrecision(); static final AtSignLogger logger = AtSignLogger('AtMetadataBuilder'); @@ -18,26 +16,26 @@ class AtMetadataBuilder { /// ttb : Time to birth of the key. If ttb is null, atMetadata's ttb is assigned to ttb. /// ttr : Time to refresh of the key. If ttr is null, atMetadata's ttr is assigned to ttr. /// ccd : Cascade delete. If ccd is null, atMetadata's ccd is assigned to ccd. - AtMetadataBuilder({ - String? atSign, - AtMetaData? newAtMetaData, - AtMetaData? existingMetaData, - int? ttl, - int? ttb, - int? ttr, - bool? ccd, - bool? isBinary, - bool? isEncrypted, - String? dataSignature, - String? sharedKeyEncrypted, - String? publicKeyChecksum, - String? encoding, - String? encKeyName, - String? encAlgo, - String? ivNonce, - String? skeEncKeyName, - String? skeEncAlgo, - }) { + AtMetadataBuilder( + {String? atSign, + AtMetaData? newAtMetaData, + AtMetaData? existingMetaData, + int? ttl, + int? ttb, + int? ttr, + bool? ccd, + bool? isBinary, + bool? isEncrypted, + String? dataSignature, + String? sharedKeyEncrypted, + String? publicKeyChecksum, + String? encoding, + String? encKeyName, + String? encAlgo, + String? ivNonce, + String? skeEncKeyName, + String? skeEncAlgo, + }) { newAtMetaData ??= AtMetaData(); atMetaData = newAtMetaData; // createdAt indicates the date and time of the key created. @@ -60,10 +58,20 @@ class AtMetadataBuilder { ? atMetaData.version = 0 : atMetaData.version = (existingMetaData!.version! + 1); + //If new metadata is available, consider new metadata, else if existing metadata is available consider it. ttl ??= newAtMetaData.ttl; + if (ttl == null && existingMetaData != null) ttl = existingMetaData.ttl; + ttb ??= newAtMetaData.ttb; + if (ttb == null && existingMetaData != null) ttb = existingMetaData.ttb; + ttr ??= newAtMetaData.ttr; + if (ttr == null && existingMetaData != null) ttr = existingMetaData.ttr; + ccd ??= newAtMetaData.isCascade; + if (ccd == null && existingMetaData != null) { + ccd = existingMetaData.isCascade; + } isBinary ??= newAtMetaData.isBinary; isEncrypted ??= newAtMetaData.isEncrypted; dataSignature ??= newAtMetaData.dataSignature; @@ -105,27 +113,24 @@ class AtMetadataBuilder { void setTTL(int? ttl, {int? ttb}) { if (ttl != null) { atMetaData.ttl = ttl; - atMetaData.expiresAt = _getExpiresAt( - currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttl, - ttb: ttb); + atMetaData.expiresAt = + _getExpiresAt(currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttl, ttb: ttb); } } void setTTB(int? ttb) { if (ttb != null) { atMetaData.ttb = ttb; - atMetaData.availableAt = _getAvailableAt( - currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttb); - logger - .finer('setTTB($ttb) - set availableAt to ${atMetaData.availableAt}'); + atMetaData.availableAt = + _getAvailableAt(currentUtcTimeToMillisecondPrecision.millisecondsSinceEpoch, ttb); + logger.finer('setTTB($ttb) - set availableAt to ${atMetaData.availableAt}'); } } void setTTR(int? ttr) { if (ttr != null) { atMetaData.ttr = ttr; - atMetaData.refreshAt = - _getRefreshAt(currentUtcTimeToMillisecondPrecision, ttr); + atMetaData.refreshAt = _getRefreshAt(currentUtcTimeToMillisecondPrecision, ttr); } } diff --git a/packages/at_persistence_secondary_server/pubspec.yaml b/packages/at_persistence_secondary_server/pubspec.yaml index 30706d9a7..60f15aa28 100644 --- a/packages/at_persistence_secondary_server/pubspec.yaml +++ b/packages/at_persistence_secondary_server/pubspec.yaml @@ -1,6 +1,6 @@ name: at_persistence_secondary_server description: A Dart library with the implementation classes for the persistence layer of the secondary server. -version: 3.0.53 +version: 3.0.52 repository: https://github.com/atsign-foundation/at_server homepage: https://atsign.dev From c0168d76155d9faf1f06dccd3161308f78079e4c Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Thu, 6 Apr 2023 11:45:07 +0530 Subject: [PATCH 4/5] fix: Rename the method --- .../lib/src/verb/handler/abstract_update_verb_handler.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart b/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart index 36122e439..5052813c8 100644 --- a/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart +++ b/packages/at_secondary_server/lib/src/verb/handler/abstract_update_verb_handler.dart @@ -110,7 +110,7 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { atData.metaData = AtMetaData.fromCommonsMetadata(updateParams.metadata!); atData.metaData = - _setNullOrExistingMetadata(atData.metaData!, existingAtMetaData); + _unsetOrRetainMetadata(atData.metaData!, existingAtMetaData); notify( sharedBy, @@ -205,7 +205,7 @@ abstract class AbstractUpdateVerbHandler extends ChangeVerbHandler { /// If metadata contains "null" string, then reset the metadata. So set it to null /// If metadata contains null (null object), then fetch the existing metadata.If /// existing metadata value is not null, set it the current AtMetaData obj. - AtMetaData _setNullOrExistingMetadata( + AtMetaData _unsetOrRetainMetadata( AtMetaData newAtMetadata, AtMetaData? existingAtMetadata) { if (existingAtMetadata == null) { return newAtMetadata; From b3bc23b0c44dbe9c99c5f6036efaeaa240c8bbfa Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Thu, 6 Apr 2023 19:44:11 +0530 Subject: [PATCH 5/5] fix: A unit test to verify existing metadata is retained --- .../test/update_verb_test.dart | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/at_secondary_server/test/update_verb_test.dart b/packages/at_secondary_server/test/update_verb_test.dart index 47668929b..91c63dbf5 100644 --- a/packages/at_secondary_server/test/update_verb_test.dart +++ b/packages/at_secondary_server/test/update_verb_test.dart @@ -881,6 +881,61 @@ void main() { await doit(); } }); + + test('A test to verify existing metadata is retained after an update', + () async { + var atKey = 'email.wavi'; + var value = 'alice@atsign.com'; + var updateBuilder = UpdateVerbBuilder() + ..value = value + ..atKey = atKey + ..sharedBy = alice + ..sharedWith = bob + ..ivNonce = 'some_iv'; + var updateCommand = updateBuilder.buildCommand().trim(); + expect( + updateCommand, + 'update' + ':ivNonce:some_iv' + ':$bob:$atKey$alice $value'); + + inboundConnection.metadata.isAuthenticated = true; + // 1. Do an update and verify via llookup + UpdateVerbHandler updateHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); + await updateHandler.process(updateCommand, inboundConnection); + + LocalLookupVerbHandler llookupHandler = + LocalLookupVerbHandler(secondaryKeyStore); + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); + Map mapSentToClient = decodeResponse(inboundConnection.lastWrittenData!); + expect(mapSentToClient['key'], '$bob:$atKey$alice'); + expect(mapSentToClient['data'], value); + AtMetaData atMetaData = AtMetaData.fromJson(mapSentToClient['metaData']); + expect(atMetaData.ivNonce, 'some_iv'); + + // 2. Update the metadata of a different metadata attribute + updateBuilder = UpdateVerbBuilder() + ..value = value + ..atKey = atKey + ..sharedBy = alice + ..sharedWith = bob + ..sharedKeyEncrypted = 'shared_key_encrypted'; + updateCommand = updateBuilder.buildCommand().trim(); + updateHandler = UpdateVerbHandler( + secondaryKeyStore, statsNotificationService, notificationManager); + await updateHandler.process(updateCommand, inboundConnection); + + await llookupHandler.process( + 'llookup:all:$bob:$atKey$alice', inboundConnection); + mapSentToClient = decodeResponse(inboundConnection.lastWrittenData!); + expect(mapSentToClient['key'], '$bob:$atKey$alice'); + expect(mapSentToClient['data'], value); + atMetaData = AtMetaData.fromJson(mapSentToClient['metaData']); + expect(atMetaData.ivNonce, 'some_iv'); + expect(atMetaData.sharedKeyEnc, 'shared_key_encrypted'); + }); }); group('A group of tests to validate sharedBy atsign', () {