From 0c1d069856128e66096fe28294ac7992e60c6102 Mon Sep 17 00:00:00 2001 From: Dat PHAM HOANG Date: Mon, 14 Mar 2022 12:15:40 +0700 Subject: [PATCH] TF-320 Add capability validator for Jmap request --- lib/main/error/capability_validator.dart | 20 ++ lib/main/error/request_error.dart | 11 + .../main/error/capability_validator_test.dart | 219 ++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 lib/main/error/capability_validator.dart create mode 100644 lib/main/error/request_error.dart create mode 100644 test/main/error/capability_validator_test.dart diff --git a/lib/main/error/capability_validator.dart b/lib/main/error/capability_validator.dart new file mode 100644 index 000000000..f7ab46faa --- /dev/null +++ b/lib/main/error/capability_validator.dart @@ -0,0 +1,20 @@ +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:tmail_ui_user/main/error/request_error.dart'; + +void requireCapability(Session session, List requiredCapabilities) { + final matchedCapabilities = session.capabilities.keys + .fold>( + Set(), + (previousValue, element) { + if (requiredCapabilities.contains(element)) { + previousValue.add(element); + } + return previousValue; + } + ); + final missingCapabilities = requiredCapabilities.toSet().difference(matchedCapabilities); + if (missingCapabilities.length > 0) { + throw SessionMissingCapability(missingCapabilities); + } +} \ No newline at end of file diff --git a/lib/main/error/request_error.dart b/lib/main/error/request_error.dart new file mode 100644 index 000000000..1cbaaf500 --- /dev/null +++ b/lib/main/error/request_error.dart @@ -0,0 +1,11 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; + +class SessionMissingCapability extends Equatable implements Exception { + final Set capabilityIdentifiers; + + SessionMissingCapability(this.capabilityIdentifiers); + + @override + List get props => [capabilityIdentifiers]; +} \ No newline at end of file diff --git a/test/main/error/capability_validator_test.dart b/test/main/error/capability_validator_test.dart new file mode 100644 index 000000000..570271289 --- /dev/null +++ b/test/main/error/capability_validator_test.dart @@ -0,0 +1,219 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/account/account.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/default_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mail_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mdn_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/submission_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/vacation_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/websocket_capability.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/core/sort/collation_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:tmail_ui_user/main/error/capability_validator.dart'; +import 'package:tmail_ui_user/main/error/request_error.dart'; + +void main() { + group('capability_validator_test', () { + test('when session missing one capability exception should throw', () { + final session = Session( + { + CapabilityIdentifier.jmapSubmission: + SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true), + CapabilityIdentifier.jmapWebSocket: + WebSocketCapability(true, Uri.parse('ws://domain.com/jmap/ws')), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:quota')): + DefaultCapability(Map()), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:shares')): + DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }, + { + AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): + Account(AccountName('bob@domain.tld'), true, false, { + CapabilityIdentifier.jmapSubmission: + SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, Uri.parse('ws://domain.com/jmap/ws')), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {CollationIdentifier("i;unicode-casemap")}), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:quota')): + DefaultCapability(Map()), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:shares')): + DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }) + }, + { + CapabilityIdentifier.jmapSubmission: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapWebSocket: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier( + Uri.parse( + 'urn:apache:james:params:jmap:mail:quota')): AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier( + Uri.parse( + 'urn:apache:james:params:jmap:mail:shares')): AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMdn: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse( + 'http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse( + 'http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943')); + expect( + () => requireCapability(session, [CapabilityIdentifier.jmapCore, CapabilityIdentifier.jmapMail]), + throwsA(TypeMatcher())); + }); + + test('when session have all required capabilities exception should throw', () { + final session = Session( + { + CapabilityIdentifier.jmapSubmission: + SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {CollationIdentifier("i;unicode-casemap")} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true), + CapabilityIdentifier.jmapWebSocket: + WebSocketCapability(true, Uri.parse('ws://domain.com/jmap/ws')), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:quota')): + DefaultCapability(Map()), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:shares')): + DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }, + { + AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): + Account(AccountName('bob@domain.tld'), true, false, { + CapabilityIdentifier.jmapSubmission: + SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, Uri.parse('ws://domain.com/jmap/ws')), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {CollationIdentifier("i;unicode-casemap")}), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:quota')): + DefaultCapability(Map()), + CapabilityIdentifier( + Uri.parse('urn:apache:james:params:jmap:mail:shares')): + DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }) + }, + { + CapabilityIdentifier.jmapSubmission: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapWebSocket: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier( + Uri.parse( + 'urn:apache:james:params:jmap:mail:quota')): AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier( + Uri.parse( + 'urn:apache:james:params:jmap:mail:shares')): AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMdn: AccountId(Id( + '29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse( + 'http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse( + 'http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943') + ); + + expect( + () => requireCapability(session, [CapabilityIdentifier.jmapCore, CapabilityIdentifier.jmapMail]), + returnsNormally); + }); + }); +} \ No newline at end of file