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

feat: add optional param encryptValue to notify method #1391

Merged
merged 14 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions packages/at_client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 3.2.1
- feat: add optional param `encryptValue` to notify method
- build[deps]: Upgraded dependencies for the following packages:
- at_commons to v4.1.1
- at_utils to v3.0.18
- at_lookup to v3.0.48
- at_auth to v2.0.5
- at_persistence_secondary_server to v3.0.63
## 3.2.0
- feat: add `allowAll` flag (defaults to false) to AtRpc
## 3.1.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class AtClientConfig {

/// Represents the at_client version.
/// Must always be the same as the actual version in pubspec.yaml
final String atClientVersion = '3.2.0';
final String atClientVersion = '3.2.1';

/// Represents the client commit log compaction time interval
///
Expand Down
2 changes: 2 additions & 0 deletions packages/at_client/lib/src/service/notification_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ abstract class NotificationService {
true, // this was the behaviour before introducing this parameter
bool checkForFinalDeliveryStatus =
true, // this was the behaviour before introducing this parameter
bool encryptValue =
true, // this was the behaviour before introducing this parameter
Function(NotificationResult)? onSuccess,
Function(NotificationResult)? onError,
Function(NotificationResult)? onSentToSecondary});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ class NotificationServiceImpl
true, // this was the behaviour before introducing this parameter
bool checkForFinalDeliveryStatus =
true, // this was the behaviour before introducing this parameter
bool encryptValue =
true, // this was the behaviour before introducing this parameter
Function(NotificationResult)? onSuccess,
Function(NotificationResult)? onError,
Function(NotificationResult)? onSentToSecondary}) async {
Expand All @@ -331,6 +333,7 @@ class NotificationServiceImpl
}

try {
notificationParams.atKey.metadata.isEncrypted = encryptValue;
// If sharedBy atSign is null, default to current atSign.
if (notificationParams.atKey.sharedBy.isNull) {
notificationParams.atKey.sharedBy = _atClient.getCurrentAtSign();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ class NotificationRequestTransformer
// prepares notification builder
NotifyVerbBuilder builder = await _prepareNotificationBuilder(
notificationParams, atClientPreference);
// If notification value is not null, encrypt the value
if (notificationParams.value.isNotNull) {
// If notification value is set and metadata.isEncrypted is true, encrypt
// the value.
if (notificationParams.value.isNotNull &&
notificationParams.atKey.metadata.isEncrypted) {
builder.value = await _encryptNotificationValue(
notificationParams.atKey, notificationParams.value!);
} else {
builder.value = notificationParams.value;
}
// add metadata to notify verb builder.
// Encrypt the data and then call addMetadataToBuilder method inorder to
Expand Down
12 changes: 6 additions & 6 deletions packages/at_client/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: The at_client library is the non-platform specific Client SDK which
##
##
## NB: When incrementing the version, please also increment the version in AtClientConfig file
version: 3.2.0
version: 3.2.1
## NB: When incrementing the version, please also increment the version in AtClientConfig file
##

Expand All @@ -31,13 +31,13 @@ dependencies:
async: ^2.9.0
at_utf7: ^1.0.0
at_base2e15: ^1.0.0
at_commons: ^4.0.6
at_utils: ^3.0.16
at_commons: ^4.1.1
at_utils: ^3.0.18
at_chops: ^2.0.0
at_lookup: ^3.0.46
at_auth: ^2.0.4
at_lookup: ^3.0.48
at_auth: ^2.0.5
at_persistence_spec: ^2.0.14
at_persistence_secondary_server: ^3.0.62
at_persistence_secondary_server: ^3.0.63
meta: ^1.8.0
version: ^3.0.2

Expand Down
98 changes: 94 additions & 4 deletions packages/at_client/test/notification_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class MockLocalSecondary extends Mock implements LocalSecondary {
SecondaryKeyStore? keyStore = MockSecondaryKeyStore();
}

class MockRemoteSecondary extends Mock implements RemoteSecondary {}

class MockAtClientImpl extends Mock implements AtClientImpl {
@override
String? getCurrentAtSign() {
Expand Down Expand Up @@ -103,8 +105,7 @@ void main() {
AtKeyEncryptionManager mockAtKeyEncryptionManager =
MockAtKeyEncryptionManager();
AtLookupImpl mockAtLookupImpl = MockAtLookupImpl();

group('A group of test to validate notification request processor', () {
group('A group of test to validate notification request transformer', () {
var value = '+91908909933';
late SharedKeyEncryption mockSharedKeyEncryptionImpl;
setUp(() {
Expand All @@ -131,7 +132,7 @@ void main() {
});

test(
'A test to validate notification request with value return verb builder',
'A test to validate notification request with unencrypted value return verb builder',
() async {
var notificationParams = NotificationParams.forUpdate(
(AtKey.shared('phone', namespace: 'wavi')..sharedWith('@bob'))
Expand All @@ -152,9 +153,37 @@ void main() {
expect(notifyVerbBuilder.messageType, MessageTypeEnum.key);
expect(notifyVerbBuilder.priority, PriorityEnum.high);
expect(notifyVerbBuilder.strategy, StrategyEnum.latest);
expect(notifyVerbBuilder.value, value);
expect(notifyVerbBuilder.notifier, 'test-notifier');
expect(notifyVerbBuilder.latestN, 2);
expect(notifyVerbBuilder.ttln, Duration(minutes: 1).inMilliseconds);
});

test(
'A test to validate notification request with encrypted value return verb builder',
() async {
var notificationParams = NotificationParams.forUpdate(
(AtKey.shared('phone', namespace: 'wavi')..sharedWith('@bob'))
.build(),
value: value,
priority: PriorityEnum.high,
strategy: StrategyEnum.latest,
notifier: 'test-notifier',
latestN: 2,
notificationExpiry: Duration(minutes: 1));
notificationParams.atKey.metadata.isEncrypted = true;
var notifyVerbBuilder = await NotificationRequestTransformer(
'@alice',
AtClientPreference()..namespace = 'wavi',
mockSharedKeyEncryptionImpl)
.transform(notificationParams);
expect(notifyVerbBuilder.atKey.key, 'phone.wavi');
expect(notifyVerbBuilder.atKey.sharedWith, '@bob');
expect(notifyVerbBuilder.messageType, MessageTypeEnum.key);
expect(notifyVerbBuilder.priority, PriorityEnum.high);
expect(notifyVerbBuilder.strategy, StrategyEnum.latest);
expect(notifyVerbBuilder.value, 'encryptedValue');
expect(notifyVerbBuilder.atKey.metadata.sharedKeyEnc, 'sharedKeyEnc');
expect(notifyVerbBuilder.atKey.metadata.pubKeyCS, 'publicKeyCS');
expect(notifyVerbBuilder.notifier, 'test-notifier');
expect(notifyVerbBuilder.latestN, 2);
expect(notifyVerbBuilder.ttln, Duration(minutes: 1).inMilliseconds);
Expand Down Expand Up @@ -221,6 +250,67 @@ void main() {
e is SecondaryConnectException &&
e.message == 'Unable to connect to secondary server')));
});

test(
'A test to validate encrypted value and shared encryption key is set in verb builder when isEncrypted is set to true in metadata',
() async {
var notificationParams = NotificationParams.forUpdate(
(AtKey.shared('phone', namespace: 'wavi')..sharedWith('@bob'))
.build(),
value: value,
priority: PriorityEnum.high,
strategy: StrategyEnum.latest,
notifier: 'test-notifier',
latestN: 2,
notificationExpiry: Duration(minutes: 1));
notificationParams.atKey.metadata.isEncrypted = true;
var notifyVerbBuilder = await NotificationRequestTransformer(
'@alice',
AtClientPreference()..namespace = 'wavi',
mockSharedKeyEncryptionImpl)
.transform(notificationParams);
expect(notifyVerbBuilder.value, 'encryptedValue');
expect(notifyVerbBuilder.atKey.metadata.sharedKeyEnc, 'sharedKeyEnc');
});
test(
'A test to validate unencrypted value is set in verb builder when isEncrypted is set to false in metadata',
() async {
var notificationParams = NotificationParams.forUpdate(
(AtKey.shared('phone', namespace: 'wavi')..sharedWith('@bob'))
.build(),
value: value,
priority: PriorityEnum.high,
strategy: StrategyEnum.latest,
notifier: 'test-notifier',
latestN: 2,
notificationExpiry: Duration(minutes: 1));
notificationParams.atKey.metadata.isEncrypted = false;
var notifyVerbBuilder = await NotificationRequestTransformer(
'@alice',
AtClientPreference()..namespace = 'wavi',
mockSharedKeyEncryptionImpl)
.transform(notificationParams);
expect(notifyVerbBuilder.value, value);
});
test(
'A test to validate unencrypted value is set in verb builder when isEncrypted is NOT set in metadata',
() async {
var notificationParams = NotificationParams.forUpdate(
(AtKey.shared('phone', namespace: 'wavi')..sharedWith('@bob'))
.build(),
value: value,
priority: PriorityEnum.high,
strategy: StrategyEnum.latest,
notifier: 'test-notifier',
latestN: 2,
notificationExpiry: Duration(minutes: 1));
var notifyVerbBuilder = await NotificationRequestTransformer(
'@alice',
AtClientPreference()..namespace = 'wavi',
mockSharedKeyEncryptionImpl)
.transform(notificationParams);
expect(notifyVerbBuilder.value, value);
});
});

group('A group of test to validate notification response transformer', () {
Expand Down
1 change: 1 addition & 0 deletions packages/at_client/test/test_utils/no_op_services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class NoOpNotificationService implements NotificationService {
Future<NotificationResult> notify(NotificationParams notificationParams,
{bool waitForFinalDeliveryStatus = true,
bool checkForFinalDeliveryStatus = true,
bool encryptValue = true,
Function(NotificationResult p1)? onSuccess,
Function(NotificationResult p1)? onError,
Function(NotificationResult p1)? onSentToSecondary}) {
Expand Down
44 changes: 44 additions & 0 deletions tests/at_end2end_test/test/notify_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,49 @@ void main() async {
expect(notificationListJson[0]['from'], currentAtSign);
expect(notificationListJson[0]['to'], sharedWithAtSign);
expect(notificationListJson[0]['value'], isNotEmpty);
expect(notificationListJson[0]['value'] != value,
true); //encrypted value should be different from actual value
});
test('Notify a key with value by setting encryptValue to false', () async {
var uuid = Uuid();
// Generate uuid
var randomValue = uuid.v4();
var phoneKey = AtKey()
..key = 'phone$randomValue'
..sharedWith = sharedWithAtSign
..metadata = (Metadata()..ttr = 60000)
..namespace = namespace;

// Appending a random number as a last number to generate a new phone number
// for each run.
var value = '+1 100 200 30';
// Setting currentAtSign atClient instance to context.
currentAtClientManager = await AtClientManager.getInstance()
.setCurrentAtSign(currentAtSign, namespace,
TestPreferences.getInstance().getPreference(currentAtSign));
final notificationResult = await currentAtClientManager
.atClient.notificationService
.notify(NotificationParams.forUpdate(phoneKey, value: value),
encryptValue: false);
expect(notificationResult, isNotNull);
expect(notificationResult.notificationStatusEnum,
NotificationStatusEnum.delivered);

// Setting sharedWithAtSign atClient instance to context.
await AtClientManager.getInstance().setCurrentAtSign(
sharedWithAtSign,
namespace,
TestPreferences.getInstance().getPreference(sharedWithAtSign));
var notificationListResult = await AtClientManager.getInstance()
.atClient
.notifyList(regex: 'phone$randomValue');
expect(notificationListResult, isNotEmpty);
notificationListResult = notificationListResult.replaceFirst('data:', '');
final notificationListJson = jsonDecode(notificationListResult);
print(notificationListJson);
expect(notificationListJson[0]['from'], currentAtSign);
expect(notificationListJson[0]['to'], sharedWithAtSign);
expect(notificationListJson[0]['isEncrypted'], false);
expect(notificationListJson[0]['value'], value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also add an expect for isEncrypted please?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am getting isEncrypted=true for this test.
[{id: 79a2d96e-ff34-442c-aac0-97a556fcbcfc, from: @ce2e1, to: @ce2e2, key: @ce2e2:phone2a9c23ff-cb4c-4d41-a50d-276d87d24820.e2e_test@ce2e1, value: +1 100 200 30, operation: update, epochMillis: 1726120153595, messageType: MessageType.key, isEncrypted: true, metadata: {encKeyName: null, encAlgo: null, ivNonce: null, skeEncKeyName: null, skeEncAlgo: null, availableAt: null, expiresAt: 2024-09-13 05:49:13.595Z***]
Is this because we don't have the changes on the server yet?

});
}
33 changes: 25 additions & 8 deletions tests/at_functional_test/test/atclient_notify_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,29 @@ void main() {
..sharedWith = sharedWithAtSign
..namespace = namespace;
var value = '+1 100 200 300';
await atClientManager.atClient.notificationService
var notificationResult = await atClientManager.atClient.notificationService
.notify(NotificationParams.forUpdate(phoneKey, value: value));
var notification = await atClientManager.atClient.notificationService
.fetch(notificationResult.notificationID);
print(notification.value);
// encrypted value should not be equal to actual value
expect(notification.value == value, false);
});

test('verify unencrypted value is returned when encryptValue is set to false',
() async {
var phoneKey = AtKey()
..key = 'phone'
..sharedWith = sharedWithAtSign
..namespace = namespace;
var value = '+1 100 200 300';
var notificationResult = await atClientManager.atClient.notificationService
.notify(NotificationParams.forUpdate(phoneKey, value: value),
encryptValue: false);
var notificationId = notificationResult.notificationID;
var notification = await atClientManager.atClient.notificationService
.fetch(notificationId);
expect(notification.value, value);
});

test('notify deletion of a key to sharedWith atSign', () async {
Expand Down Expand Up @@ -217,12 +238,10 @@ void main() {
}
});

group('A group of tests for notification fetch', () {
group('A group of tests for notification fetch', () {
test('A test to verify non existent notification', () async {
await AtClientManager.getInstance().setCurrentAtSign(
currentAtSign,
namespace,
TestUtils.getPreference(currentAtSign));
currentAtSign, namespace, TestUtils.getPreference(currentAtSign));
var notificationResult = await AtClientManager.getInstance()
.atClient
.notificationService
Expand All @@ -235,9 +254,7 @@ void main() {
for (int i = 0; i < 10; i++) {
print('Testing notification expiry - test run #$i');
await AtClientManager.getInstance().setCurrentAtSign(
currentAtSign,
namespace,
TestUtils.getPreference(currentAtSign));
currentAtSign, namespace, TestUtils.getPreference(currentAtSign));
var atKey = (AtKey.shared('test-notification-expiry',
namespace: 'wavi', sharedBy: currentAtSign)
..sharedWith(sharedWithAtSign))
Expand Down
Loading