diff --git a/packages/at_lookup/CHANGELOG.md b/packages/at_lookup/CHANGELOG.md index 7e310f80..8e9f15d9 100644 --- a/packages/at_lookup/CHANGELOG.md +++ b/packages/at_lookup/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.0.48 +- feat: consume EnrollVerbBuilder in AtLookup.executeVerb() +- chore: upgrade at_commons to v4.1.1 and at_utils to v3.0.18 ## 3.0.47 - fix: Fixed legacy error handling so error message isn't truncated if it contains a hyphen diff --git a/packages/at_lookup/example/bin/example.dart b/packages/at_lookup/example/bin/example.dart index 20588436..69c3a3b6 100644 --- a/packages/at_lookup/example/bin/example.dart +++ b/packages/at_lookup/example/bin/example.dart @@ -18,7 +18,8 @@ void main() async { /// To update a key into secondary server //Build update verb builder var updateVerbBuilder = UpdateVerbBuilder() - ..atKey = (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build() + ..atKey = + (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build() ..value = '+1 889 886 7879'; // Sends update command to secondary server @@ -38,12 +39,14 @@ void main() async { /// To retrieve the value of key created by self. var lLookupVerbBuilder = LLookupVerbBuilder() - ..atKey = (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); + ..atKey = + (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); await atLookupImpl.executeVerb(lLookupVerbBuilder); ///To remove a key from secondary server var deleteVerbBuilder = DeleteVerbBuilder() - ..atKey = (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); + ..atKey = + (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); await atLookupImpl.executeVerb(deleteVerbBuilder, sync: true); /// To retrieve keys from the secondary server @@ -52,7 +55,8 @@ void main() async { ///To notify key to another atSign var notifyVerbBuilder = NotifyVerbBuilder() - ..atKey = (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); + ..atKey = + (AtKey.shared('phone', sharedBy: '@alice')..sharedWith('@bob')).build(); await atLookupImpl.executeVerb(notifyVerbBuilder); ///To retrieve the notifications received diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index f5cfc668..550c1801 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -179,7 +179,8 @@ class AtLookupImpl implements AtLookUp { logger.finer('value: $value dataSignature:$dataSignature'); var isDataValid = publicKey.verifySHA256Signature( // ignore: unnecessary_cast - utf8.encode(value) as Uint8List, base64Decode(dataSignature)); + utf8.encode(value) as Uint8List, + base64Decode(dataSignature)); logger.finer('data verify result: $isDataValid'); return 'data:$value'; } on Exception catch (e) { @@ -296,6 +297,8 @@ class AtLookupImpl implements AtLookUp { verbResult = await _notifyRemove(builder); } else if (builder is NotifyFetchVerbBuilder) { verbResult = await _notifyFetch(builder); + } else if (builder is EnrollVerbBuilder) { + verbResult = await _enroll(builder); } } on Exception catch (e) { logger.severe('Error in remote verb execution ${e.toString()}'); @@ -336,7 +339,7 @@ class AtLookupImpl implements AtLookUp { // TODO: Can we remove the below catch block in next release once all the servers are migrated to new version. if (verbResult.contains('-')) { errorCode = verbResult.substring(0, verbResult.indexOf('-')); - errorDescription = verbResult.substring(verbResult.indexOf('-')+1); + errorDescription = verbResult.substring(verbResult.indexOf('-') + 1); } else { errorDescription += ": $verbResult"; } @@ -407,6 +410,14 @@ class AtLookupImpl implements AtLookUp { return await _process(atCommand, auth: true); } + Future _enroll(EnrollVerbBuilder builder) async { + var atCommand = builder.buildCommand(); + if (builder.operation == EnrollOperationEnum.request) { + return _process(atCommand, auth: false); + } + return await _process(atCommand, auth: true); + } + @override Future executeCommand(String atCommand, {bool auth = false}) async { String verbResponse = await _process(atCommand, auth: auth); diff --git a/packages/at_lookup/pubspec.yaml b/packages/at_lookup/pubspec.yaml index 9da24553..caf17e64 100644 --- a/packages/at_lookup/pubspec.yaml +++ b/packages/at_lookup/pubspec.yaml @@ -1,6 +1,6 @@ name: at_lookup description: A Dart library that contains the core commands that can be used with a secondary server (scan, update, lookup, llookup, plookup, etc.) -version: 3.0.47 +version: 3.0.48 repository: https://github.com/atsign-foundation/at_libraries homepage: https://atsign.com documentation: https://docs.atsign.com/ @@ -12,8 +12,8 @@ dependencies: path: ^1.8.0 crypton: ^2.0.1 crypto: ^3.0.1 - at_utils: ^3.0.16 - at_commons: ^4.0.0 + at_utils: ^3.0.18 + at_commons: ^4.1.1 mutex: ^3.0.0 meta: ^1.8.0 at_chops: ^2.0.0 diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index 2beb08f4..c248089f 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -1,8 +1,11 @@ +import 'dart:async'; import 'dart:io'; import 'package:at_chops/at_chops.dart'; +import 'package:at_commons/at_builders.dart'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; import 'package:test/test.dart'; import 'package:mocktail/mocktail.dart'; @@ -212,6 +215,7 @@ void main() { e is UnAuthenticatedException && e.message.contains('AT0401')))); }); }); + group('A group of tests to verify executeCommand method', () { test('executeCommand - from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, @@ -226,6 +230,7 @@ void main() { var result = await atLookup.executeCommand('from:@alice\n'); expect(result, fromResponse); }); + test('executeCommand -llookup verb - auth true - auth key not set', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, @@ -292,7 +297,8 @@ void main() { outboundConnectionFactory: mockOutboundConnectionFactory); atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; - final llookupResponse = 'error:{"errorCode":"AT0015","errorDescription":"Exception: fubar"}'; + final llookupResponse = + 'error:{"errorCode":"AT0015","errorDescription":"Exception: fubar"}'; when(() => mockOutBoundConnection.write(llookupCommand)) .thenAnswer((invocation) { mockSecureSocket.write(llookupCommand); @@ -306,4 +312,146 @@ void main() { e is AtLookUpException && e.errorMessage == 'Exception: fubar'))); }); }); + + group('Validate executeVerb() behaviour', () { + test('validate EnrollVerbHandler behaviour - request', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + secureSocketFactory: mockSocketFactory, + socketListenerFactory: mockSecureSocketListenerFactory, + outboundConnectionFactory: mockOutboundConnectionFactory); + + String appName = 'unit_test_1'; + String deviceName = 'test_device'; + String otp = 'ABCDEF'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.request + ..appName = appName + ..deviceName = deviceName + ..otp = otp; + String enrollCommand = + 'enroll:request:{"appName":"$appName","deviceName":"$deviceName","otp":"$otp"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1234567890","status":"pending"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() + ..isAuthenticated = false; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + var result = await atLookup.executeVerb(enrollVerbBuilder); + expect(result, enrollResponse); + }); + + test('validate behaviour with EnrollVerbHandler - approve', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + secureSocketFactory: mockSocketFactory, + socketListenerFactory: mockSecureSocketListenerFactory, + outboundConnectionFactory: mockOutboundConnectionFactory); + atLookup.atChops = mockAtChops; + + String appName = 'unit_test_2'; + String deviceName = 'test_device'; + String enrollmentId = '1357913579'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.approve + ..enrollmentId = '1357913579' + ..appName = appName + ..deviceName = deviceName; + String enrollCommand = + 'enroll:approve:{"enrollmentId":"$enrollmentId","appName":"$appName","deviceName":"$deviceName"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1357913579","status":"approved"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() + ..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); + }); + + test('validate behaviour with EnrollVerbHandler - revoke', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + secureSocketFactory: mockSocketFactory, + socketListenerFactory: mockSecureSocketListenerFactory, + outboundConnectionFactory: mockOutboundConnectionFactory); + atLookup.atChops = mockAtChops; + String enrollmentId = '89213647826348'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.revoke + ..enrollmentId = enrollmentId; + String enrollCommand = 'enroll:revoke:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() + ..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); + }); + + test('validate behaviour with EnrollVerbHandler - deny', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + secureSocketFactory: mockSocketFactory, + socketListenerFactory: mockSecureSocketListenerFactory, + outboundConnectionFactory: mockOutboundConnectionFactory); + atLookup.atChops = mockAtChops; + String enrollmentId = '5754765754'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.deny + ..enrollmentId = enrollmentId; + String enrollCommand = 'enroll:deny:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() + ..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); + }); + }); }