@rapidmx/activesync-plugin 1.0.0-beta.2 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/README.md +4 -4
  2. package/dist/lib/BaseDeviceSyncStateRoute.js +32 -0
  3. package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -1
  4. package/dist/lib/BaseEasRoute.js +91 -15
  5. package/dist/lib/BaseEasRoute.js.map +1 -1
  6. package/dist/lib/EasAuditLog.js +56 -0
  7. package/dist/lib/EasAuditLog.js.map +1 -0
  8. package/dist/lib/EasCollectionLease.js +199 -0
  9. package/dist/lib/EasCollectionLease.js.map +1 -0
  10. package/dist/lib/EasCollectionStore.js +102 -0
  11. package/dist/lib/EasCollectionStore.js.map +1 -0
  12. package/dist/lib/EasCollectionSync.js +294 -0
  13. package/dist/lib/EasCollectionSync.js.map +1 -0
  14. package/dist/lib/EasSyncKeyUtils.js +125 -62
  15. package/dist/lib/EasSyncKeyUtils.js.map +1 -1
  16. package/dist/lib/MessageMoveRules.js +100 -0
  17. package/dist/lib/MessageMoveRules.js.map +1 -0
  18. package/dist/lib/MimeHeaderUtils.js +395 -0
  19. package/dist/lib/MimeHeaderUtils.js.map +1 -0
  20. package/dist/lib/RegexPatternUtils.js +30 -0
  21. package/dist/lib/RegexPatternUtils.js.map +1 -0
  22. package/dist/lib/RestapiCompat.js +41 -0
  23. package/dist/lib/RestapiCompat.js.map +1 -0
  24. package/dist/lib/adapters/CalendarSyncAdapter.js +142 -33
  25. package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -1
  26. package/dist/lib/adapters/EmailSyncAdapter.js +114 -27
  27. package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -1
  28. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +2 -1
  29. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -1
  30. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +2 -1
  31. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -1
  32. package/dist/lib/codec/WbxmlDecoder.js +86 -34
  33. package/dist/lib/codec/WbxmlDecoder.js.map +1 -1
  34. package/dist/lib/codec/WbxmlEncoder.js +103 -22
  35. package/dist/lib/codec/WbxmlEncoder.js.map +1 -1
  36. package/dist/lib/commands/ComposeMailCommand.js +105 -31
  37. package/dist/lib/commands/ComposeMailCommand.js.map +1 -1
  38. package/dist/lib/commands/FolderSyncCommand.js +67 -27
  39. package/dist/lib/commands/FolderSyncCommand.js.map +1 -1
  40. package/dist/lib/commands/GetItemEstimateCommand.js +80 -59
  41. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -1
  42. package/dist/lib/commands/ItemOperationsCommand.js +219 -56
  43. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -1
  44. package/dist/lib/commands/MeetingResponseCommand.js +212 -53
  45. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -1
  46. package/dist/lib/commands/MoveItemsCommand.js +54 -21
  47. package/dist/lib/commands/MoveItemsCommand.js.map +1 -1
  48. package/dist/lib/commands/PingCommand.js +288 -56
  49. package/dist/lib/commands/PingCommand.js.map +1 -1
  50. package/dist/lib/commands/ProvisionCommand.js +34 -17
  51. package/dist/lib/commands/ProvisionCommand.js.map +1 -1
  52. package/dist/lib/commands/ResolveRecipientsCommand.js +43 -5
  53. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -1
  54. package/dist/lib/commands/SearchCommand.js +92 -17
  55. package/dist/lib/commands/SearchCommand.js.map +1 -1
  56. package/dist/lib/commands/SettingsCommand.js +2 -1
  57. package/dist/lib/commands/SettingsCommand.js.map +1 -1
  58. package/dist/lib/commands/SmartForwardCommand.js +2 -1
  59. package/dist/lib/commands/SmartForwardCommand.js.map +1 -1
  60. package/dist/lib/commands/SmartReplyCommand.js +2 -1
  61. package/dist/lib/commands/SmartReplyCommand.js.map +1 -1
  62. package/dist/lib/commands/SyncCommand.js +481 -244
  63. package/dist/lib/commands/SyncCommand.js.map +1 -1
  64. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +6 -1
  65. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -1
  66. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +3 -1
  67. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -1
  68. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +2 -1
  69. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -1
  70. package/dist/lib/commands/mongo/PingCommandMongo.js +23 -0
  71. package/dist/lib/commands/mongo/PingCommandMongo.js.map +1 -0
  72. package/dist/lib/commands/mongo/SearchCommandMongo.js +3 -1
  73. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -1
  74. package/dist/lib/commands/mongo/SendMailCommandMongo.js +2 -1
  75. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -1
  76. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +2 -1
  77. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -1
  78. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +2 -1
  79. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -1
  80. package/dist/lib/commands/mongo/SyncCommandMongo.js +7 -1
  81. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -1
  82. package/dist/lib/commands/mongo/index.js +1 -0
  83. package/dist/lib/commands/mongo/index.js.map +1 -1
  84. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +6 -1
  85. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -1
  86. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +3 -1
  87. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -1
  88. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +2 -1
  89. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -1
  90. package/dist/lib/commands/sql/PingCommandSQL.js +23 -0
  91. package/dist/lib/commands/sql/PingCommandSQL.js.map +1 -0
  92. package/dist/lib/commands/sql/SearchCommandSQL.js +3 -1
  93. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -1
  94. package/dist/lib/commands/sql/SendMailCommandSQL.js +2 -1
  95. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -1
  96. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +2 -1
  97. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -1
  98. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +2 -1
  99. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -1
  100. package/dist/lib/commands/sql/SyncCommandSQL.js +7 -1
  101. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -1
  102. package/dist/lib/commands/sql/index.js +1 -0
  103. package/dist/lib/commands/sql/index.js.map +1 -1
  104. package/dist/lib/index.js +5 -0
  105. package/dist/lib/index.js.map +1 -1
  106. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +44 -2
  107. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -1
  108. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +4 -0
  109. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -1
  110. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +9 -0
  111. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -1
  112. package/dist/lib/models/EasCollectionChunk.js +2 -0
  113. package/dist/lib/models/EasCollectionChunk.js.map +1 -0
  114. package/dist/lib/models/EasCollectionState.js +2 -0
  115. package/dist/lib/models/EasCollectionState.js.map +1 -0
  116. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +7 -0
  117. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -1
  118. package/dist/lib/models/mongo/EasCollectionChunkMongo.js +83 -0
  119. package/dist/lib/models/mongo/EasCollectionChunkMongo.js.map +1 -0
  120. package/dist/lib/models/mongo/EasCollectionStateMongo.js +162 -0
  121. package/dist/lib/models/mongo/EasCollectionStateMongo.js.map +1 -0
  122. package/dist/lib/models/sql/DeviceSyncStateSQL.js +7 -0
  123. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -1
  124. package/dist/lib/models/sql/EasCollectionChunkSQL.js +83 -0
  125. package/dist/lib/models/sql/EasCollectionChunkSQL.js.map +1 -0
  126. package/dist/lib/models/sql/EasCollectionStateSQL.js +162 -0
  127. package/dist/lib/models/sql/EasCollectionStateSQL.js.map +1 -0
  128. package/dist/lib/mongo/EasRouteMongo.js +2 -2
  129. package/dist/lib/mongo/EasRouteMongo.js.map +1 -1
  130. package/dist/lib/mongo.js +2 -0
  131. package/dist/lib/mongo.js.map +1 -1
  132. package/dist/lib/sql/EasRouteSQL.js +2 -2
  133. package/dist/lib/sql/EasRouteSQL.js.map +1 -1
  134. package/dist/lib/sql.js +2 -0
  135. package/dist/lib/sql.js.map +1 -1
  136. package/dist/types/BaseDeviceSyncStateRoute.d.ts +10 -0
  137. package/dist/types/BaseEasRoute.d.ts +15 -4
  138. package/dist/types/EasAuditLog.d.ts +47 -0
  139. package/dist/types/EasCollectionLease.d.ts +60 -0
  140. package/dist/types/EasCollectionStore.d.ts +51 -0
  141. package/dist/types/EasCollectionSync.d.ts +96 -0
  142. package/dist/types/EasCommandHandler.d.ts +7 -4
  143. package/dist/types/EasSyncKeyUtils.d.ts +73 -42
  144. package/dist/types/MessageMoveRules.d.ts +45 -0
  145. package/dist/types/MimeHeaderUtils.d.ts +73 -0
  146. package/dist/types/RegexPatternUtils.d.ts +14 -0
  147. package/dist/types/RestapiCompat.d.ts +24 -0
  148. package/dist/types/adapters/CalendarSyncAdapter.d.ts +47 -18
  149. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +18 -2
  150. package/dist/types/adapters/EmailSyncAdapter.d.ts +24 -7
  151. package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +1 -0
  152. package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +1 -0
  153. package/dist/types/codec/WbxmlDecoder.d.ts +39 -0
  154. package/dist/types/codec/WbxmlEncoder.d.ts +36 -1
  155. package/dist/types/commands/ComposeMailCommand.d.ts +33 -15
  156. package/dist/types/commands/FolderSyncCommand.d.ts +4 -1
  157. package/dist/types/commands/GetItemEstimateCommand.d.ts +20 -15
  158. package/dist/types/commands/ItemOperationsCommand.d.ts +29 -4
  159. package/dist/types/commands/MeetingResponseCommand.d.ts +52 -13
  160. package/dist/types/commands/MoveItemsCommand.d.ts +13 -3
  161. package/dist/types/commands/PingCommand.d.ts +72 -14
  162. package/dist/types/commands/ProvisionCommand.d.ts +16 -11
  163. package/dist/types/commands/ResolveRecipientsCommand.d.ts +4 -0
  164. package/dist/types/commands/SearchCommand.d.ts +13 -1
  165. package/dist/types/commands/SyncCommand.d.ts +102 -62
  166. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +3 -0
  167. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +2 -0
  168. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +1 -0
  169. package/dist/types/commands/mongo/PingCommandMongo.d.ts +8 -0
  170. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +2 -0
  171. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +1 -0
  172. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +1 -0
  173. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +1 -0
  174. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +4 -0
  175. package/dist/types/commands/mongo/index.d.ts +1 -0
  176. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +3 -0
  177. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +2 -0
  178. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +1 -0
  179. package/dist/types/commands/sql/PingCommandSQL.d.ts +8 -0
  180. package/dist/types/commands/sql/SearchCommandSQL.d.ts +2 -0
  181. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +1 -0
  182. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +1 -0
  183. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +1 -0
  184. package/dist/types/commands/sql/SyncCommandSQL.d.ts +4 -0
  185. package/dist/types/commands/sql/index.d.ts +1 -0
  186. package/dist/types/index.d.ts +5 -0
  187. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +15 -0
  188. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +2 -0
  189. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +4 -0
  190. package/dist/types/models/DeviceSyncState.d.ts +13 -2
  191. package/dist/types/models/EasCollectionChunk.d.ts +20 -0
  192. package/dist/types/models/EasCollectionState.d.ts +79 -0
  193. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +1 -0
  194. package/dist/types/models/mongo/EasCollectionChunkMongo.d.ts +16 -0
  195. package/dist/types/models/mongo/EasCollectionStateMongo.d.ts +27 -0
  196. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +1 -0
  197. package/dist/types/models/sql/EasCollectionChunkSQL.d.ts +16 -0
  198. package/dist/types/models/sql/EasCollectionStateSQL.d.ts +27 -0
  199. package/dist/types/mongo.d.ts +2 -0
  200. package/dist/types/sql.d.ts +2 -0
  201. package/package.json +267 -238
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/RapidMX/activesync/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/RapidMX/activesync/actions/workflows/build.yml)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/RapidMX/activesync/badge.svg?branch=main)](https://coveralls.io/github/RapidMX/activesync?branch=main)
5
- [![npm version](https://img.shields.io/npm/v/@rapidmx/activesync)](https://www.npmjs.com/package/@rapidmx/activesync)
5
+ [![npm version](https://img.shields.io/npm/v/@rapidmx/activesync-plugin)](https://www.npmjs.com/package/@rapidmx/activesync-plugin)
6
6
 
7
7
  Exchange ActiveSync (EAS) protocol support for a [`@rapidmx/restapi`](https://github.com/RapidMX/restapi)-based
8
8
  mail server — covers every command a real mobile client (iOS Mail, Outlook mobile, Android/Samsung Mail) needs
@@ -23,7 +23,7 @@ EAS-specific login flow — which means a real native device (rather than a test
23
23
  token) needs an OAuth 2.0 Authorization Server role in front of it to obtain one; that piece is tracked as a
24
24
  follow-up in `@rapidrest/auth`, not this package.
25
25
 
26
- A [`@rapidmx/autodiscover`](https://github.com/RapidMX/autodiscover) mount lets real clients find this
26
+ A [`@rapidmx/autodiscover-plugin`](https://github.com/RapidMX/autodiscover) mount lets real clients find this
27
27
  package's endpoint from just an email address.
28
28
 
29
29
  Responds to a client's `OPTIONS` capability probe with real `MS-ASProtocolVersions`/`MS-ASProtocolCommands`
@@ -36,11 +36,11 @@ means `OPTIONS` falls back to the bare `204` and a client falls back to trying i
36
36
 
37
37
  ## Usage
38
38
 
39
- Mount `EasRouteMongo`/`EasRouteSQL` (from `@rapidmx/activesync/mongo` or `/sql`) at the protocol's well-known
39
+ Mount `EasRouteMongo`/`EasRouteSQL` (from `@rapidmx/activesync-plugin/mongo` or `/sql`) at the protocol's well-known
40
40
  path with a one-line subclass:
41
41
 
42
42
  ```ts
43
- import { EasRouteMongo } from "@rapidmx/activesync/mongo";
43
+ import { EasRouteMongo } from "@rapidmx/activesync-plugin/mongo";
44
44
  import { RouteDecorators } from "@rapidrest/service-core";
45
45
  const { Route } = RouteDecorators;
46
46
 
@@ -30,6 +30,11 @@ const { Auth, Param, Post, Request, User: AuthUser } = RouteDecorators;
30
30
  * existing 449 gate, at which point `ProvisionCommand.issuePolicy` sees the flag and sends the device a
31
31
  * `RemoteWipe` directive instead of a normal policy document - no separate push/notification channel is needed.
32
32
  *
33
+ * **Scope**: a `DeviceSyncState` row is one (mailbox, client-supplied `DeviceId`) pairing, so the wipe and the block
34
+ * that follows its acknowledgement apply to that `DeviceId` only. A compliant client wipes itself; a client that
35
+ * ignores the directive can pair again under a different `DeviceId` with the same credentials. Cutting such a client
36
+ * off requires revoking the account's credentials - not something this route does.
37
+ *
33
38
  * `deviceSyncStateClass` is supplied by the Mongo/SQL concrete subclasses, the same one-line-per-backend
34
39
  * pattern used throughout this library.
35
40
  *
@@ -69,9 +74,27 @@ export class BaseDeviceSyncStateRoute {
69
74
  remoteWipeRequested: true,
70
75
  remoteWipeAccountOnly: accountOnly,
71
76
  provisioned: false,
77
+ // The old key must stop working immediately, not just once the device next provisions.
78
+ policyKey: null,
72
79
  });
73
80
  return deviceSyncState;
74
81
  }
82
+ /**
83
+ * Lets a device that acknowledged a remote wipe (`blocked`, see `ProvisionCommand`) connect again: it must still
84
+ * complete a fresh Provision handshake. Admin-only, like `remoteWipe`.
85
+ */
86
+ async unblock(uid, user) {
87
+ if (!user || !UserUtils.hasRoles(user, this.trustedRoles)) {
88
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
89
+ }
90
+ const repo = await this.getDeviceSyncStateRepo();
91
+ const deviceSyncState = await repo.findOne(uid, { ignoreACL: true });
92
+ if (!deviceSyncState) {
93
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
94
+ }
95
+ await persistDeviceSyncState(deviceSyncState, repo, { blocked: false });
96
+ return deviceSyncState;
97
+ }
75
98
  }
76
99
  __decorate([
77
100
  Config("trusted_roles", ["admin"]),
@@ -87,4 +110,13 @@ __decorate([
87
110
  __metadata("design:paramtypes", [String, Object, Object]),
88
111
  __metadata("design:returntype", Promise)
89
112
  ], BaseDeviceSyncStateRoute.prototype, "remoteWipe", null);
113
+ __decorate([
114
+ Auth(["jwt"]),
115
+ Post("/:uid/unblock"),
116
+ __param(0, Param("uid")),
117
+ __param(1, AuthUser),
118
+ __metadata("design:type", Function),
119
+ __metadata("design:paramtypes", [String, Object]),
120
+ __metadata("design:returntype", Promise)
121
+ ], BaseDeviceSyncStateRoute.prototype, "unblock", null);
90
122
  //# sourceMappingURL=BaseDeviceSyncStateRoute.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDeviceSyncStateRoute.js","sourceRoot":"","sources":["../../src/BaseDeviceSyncStateRoute.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAiB,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAe,SAAS,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,MAAM,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;AACpC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAgB,wBAAwB;IAA9C;QAIc,iBAAY,GAAa,CAAC,OAAO,CAAC,CAAC;IAiDjD,CAAC;IA1CW,KAAK,CAAC,sBAAsB;QAChC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC5B,IAAI,CAAC,mBAAmB,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,SAAS,EAAE;gBACzE,IAAI,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI;gBACpC,IAAI,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC;aACpC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,IAAI,CAAC,mBAAmB,CAAC;IACpC,CAAC;IAED;;;;;;OAMG;IAGU,AAAN,KAAK,CAAC,UAAU,CACL,GAAW,EAChB,GAAgB,EACf,IAAc;QAExB,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,uBAAuB,EAAE,GAAG,EAAE,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;QACzG,CAAC;QAED,MAAM,IAAI,GAAiB,MAAM,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC/D,MAAM,eAAe,GAAkB,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,WAAW,GAAY,GAAG,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;QAC5D,MAAM,sBAAsB,CAAC,eAAe,EAAE,IAAI,EAAE;YAChD,mBAAmB,EAAE,IAAI;YACzB,qBAAqB,EAAE,WAAW;YAClC,WAAW,EAAE,KAAK;SACrB,CAAC,CAAC;QACH,OAAO,eAAe,CAAC;IAC3B,CAAC;CACJ;AAjDa;IADT,MAAM,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,CAAC;;8DACU;AA0BhC;IAFZ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;IACb,IAAI,CAAC,mBAAmB,CAAC;IAErB,WAAA,KAAK,CAAC,KAAK,CAAC,CAAA;IACZ,WAAA,OAAO,CAAA;IACP,WAAA,QAAQ,CAAA;;;;0DAmBZ"}
1
+ {"version":3,"file":"BaseDeviceSyncStateRoute.js","sourceRoot":"","sources":["../../src/BaseDeviceSyncStateRoute.ts"],"names":[],"mappings":"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"}
@@ -16,11 +16,11 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
16
16
  ///////////////////////////////////////////////////////////////////////////////
17
17
  import { ApiError, ObjectDecorators } from "@rapidrest/core";
18
18
  import { ApiErrorMessages, ApiErrors, RepoUtils, RouteDecorators, } from "@rapidrest/service-core";
19
- import { WbxmlDecoder } from "./codec/WbxmlDecoder.js";
19
+ import { WbxmlDecodeError, WbxmlDecoder } from "./codec/WbxmlDecoder.js";
20
20
  import { WbxmlEncoder } from "./codec/WbxmlEncoder.js";
21
21
  import { persistDeviceSyncState } from "./EasSyncKeyUtils.js";
22
22
  import { resolveCallerMailboxUid } from "@rapidmx/restapi";
23
- const { Init, Logger } = ObjectDecorators;
23
+ const { Config, Init, Logger } = ObjectDecorators;
24
24
  const { Auth, Options, Post, Request, Response, User: AuthUser } = RouteDecorators;
25
25
  /** HTTP 449 ("Retry With") is not a standard HTTP status, but is the long-established Exchange ActiveSync
26
26
  * convention a real client recognizes as "you must successfully complete `Provision` before this command will
@@ -38,9 +38,27 @@ const HTTP_STATUS_RETRY_WITH = 449;
38
38
  * `ResolveRecipients`). No MS-ASCMD `Sync`/`FolderSync`/`Provision` schema element became newly mandatory
39
39
  * between 14.1 and 16.1 outside `Oof`/IRM. Ascending, matching the order Microsoft's own spec lists them in. */
40
40
  const MS_AS_PROTOCOL_VERSIONS = "14.0,14.1,16.0,16.1";
41
+ /** Default `mail:eas:max_request_bytes` - comfortably above a real device's largest request (a `SendMail` with
42
+ * attachments), far below the host-wide body limit. The WBXML decoder's own element limits bound memory further. */
43
+ const DEFAULT_MAX_REQUEST_BYTES = 16 * 1024 * 1024;
44
+ /** Settings request elements a device may send before it is provisioned (and without a policy key): the first-run
45
+ * `UserInformation` lookup and `DeviceInformation` report. Anything else - `Oof` in particular, which writes the
46
+ * mailbox's automatic replies - needs a provisioned device. */
47
+ const SETTINGS_WITHOUT_PROVISIONING = new Set(["UserInformation", "DeviceInformation"]);
41
48
  function firstQueryValue(value) {
42
49
  return Array.isArray(value) ? value[0] : value;
43
50
  }
51
+ /** An acceptable `DeviceId`: up to 128 visible ASCII characters other than parentheses and commas. [MS-ASHTTP] only
52
+ * allows alphanumerics; this stays more tolerant of real clients while keeping the value out of the query parser's
53
+ * `op(value)` and `in(a,b)` syntax - `DeviceId` goes straight into `find()` criteria for the device's own rows. */
54
+ const DEVICE_ID_PATTERN = /^[\x21-\x27\x2a\x2b\x2d-\x7e]{1,128}$/;
55
+ /** `DeviceId` values the query parser gives a meaning of its own even without an operator: `me` becomes the caller's
56
+ * uid (or a 403 without one) and `null` becomes an IS NULL match. Both are refused like any other malformed id. */
57
+ const RESERVED_DEVICE_IDS = new Set(["me", "null"]);
58
+ /** Whether `deviceId` is an acceptable `DeviceId` (`DEVICE_ID_PATTERN`, and not one of `RESERVED_DEVICE_IDS`). */
59
+ export function isValidDeviceId(deviceId) {
60
+ return deviceId !== undefined && DEVICE_ID_PATTERN.test(deviceId) && !RESERVED_DEVICE_IDS.has(deviceId);
61
+ }
44
62
  /**
45
63
  * Abstract base for the single fixed EAS endpoint (`POST /Microsoft-Server-ActiveSync` by MS-ASHTTP
46
64
  * convention, though the concrete path is left to the consuming application to mount via `@Route(...)` — see
@@ -54,10 +72,13 @@ function firstQueryValue(value) {
54
72
  * deliberately does not implement itself — see the architecture plan's "Auth" section for the full reasoning
55
73
  * behind this choice over a per-request Basic Auth strategy.
56
74
  *
57
- * **Dispatch flow**: resolve the caller's own `Mailbox` (never a client-supplied one — `resolveCallerMailboxUid`,
58
- * the same helper `BaseSearchRoute` already uses), find-or-create that (mailbox, device) pair's
59
- * `DeviceSyncState`, enforce the provisioning gate, decode the WBXML request body (if any), dispatch to the
60
- * matching registered `EasCommandHandler`, bump `lastSyncAt`, and encode the handler's response back to WBXML.
75
+ * **Dispatch flow**: reject a body over `mail:eas:max_request_bytes` (413), resolve the caller's own `Mailbox` (never
76
+ * a client-supplied one `resolveCallerMailboxUid`), find-or-create that (mailbox, device) pair's `DeviceSyncState`,
77
+ * refuse a `blocked` device (one that acknowledged a remote wipe) with 403 for anything but `Provision` (which
78
+ * answers Status 129 itself), decode the WBXML request body (if any; malformed or over the decoder's element limits
79
+ * -> 400), enforce the provisioning gate (provisioned *and* presenting the stored policy key, else 449 - only
80
+ * `Provision` and a `Settings` request limited to `UserInformation`/`DeviceInformation` are exempt), dispatch to the matching registered
81
+ * `EasCommandHandler`, record `lastSyncAt` (best-effort), and encode the handler's response back to WBXML.
61
82
  *
62
83
  * **Command handlers** are supplied via `commandHandlerClasses` (empty by default — this class alone is just
63
84
  * the transport skeleton; concrete command support, e.g. `ProvisionCommand`/`FolderSyncCommand`, is added
@@ -84,6 +105,8 @@ export class BaseEasRoute {
84
105
  * for a transport skeleton with no commands implemented yet, not a bug to work around. */
85
106
  this.commandHandlerClasses = [];
86
107
  this.handlers = new Map();
108
+ /** Largest request body `dispatch()` accepts (HTTP 413 beyond it). */
109
+ this.maxRequestBytes = DEFAULT_MAX_REQUEST_BYTES;
87
110
  }
88
111
  async init() {
89
112
  this.deviceSyncStateRepo = await this._objectFactory.newInstance(RepoUtils, {
@@ -124,40 +147,70 @@ export class BaseEasRoute {
124
147
  const deviceId = firstQueryValue(req.query["DeviceId"]);
125
148
  const deviceType = firstQueryValue(req.query["DeviceType"]) ?? "Unknown";
126
149
  const policyKey = firstQueryValue(req.query["PolicyKey"]);
127
- if (!cmd || !deviceId) {
150
+ if (!cmd || !isValidDeviceId(deviceId)) {
128
151
  throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
129
152
  }
153
+ // service-core has no per-route body limit, so the host-wide `max_body_size` still bounds how much was
154
+ // read; this rejects anything beyond the (much lower) EAS limit before any decoding or database work.
155
+ const declaredLength = Number(firstQueryValue(req.headers["content-length"]));
156
+ if ((Number.isFinite(declaredLength) && declaredLength > this.maxRequestBytes) || (req.rawBody?.length ?? 0) > this.maxRequestBytes) {
157
+ res.status(413).send();
158
+ return;
159
+ }
130
160
  const mailboxUid = await resolveCallerMailboxUid(this.mailboxRepo, user);
131
161
  if (!mailboxUid) {
132
162
  throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
133
163
  }
134
164
  const deviceSyncState = await this.findOrCreateDeviceSyncState(mailboxUid, deviceId, deviceType);
135
- // Every command except the two that must work on an unprovisioned device (Provision itself, and
136
- // Settings - real clients query Settings/DeviceInformation as part of first-run setup, before
137
- // provisioning completes) requires the device to have already been provisioned.
138
- if (!deviceSyncState.provisioned && cmd !== "Provision" && cmd !== "Settings") {
139
- res.status(HTTP_STATUS_RETRY_WITH).send();
165
+ // A device that acknowledged a remote wipe stays locked out until an administrator unblocks it.
166
+ if (deviceSyncState.blocked && cmd !== "Provision") {
167
+ res.status(403).send();
140
168
  return;
141
169
  }
142
170
  const handler = this.handlers.get(cmd);
171
+ // Settings is decoded ahead of the provisioning gate, which needs to see what it asks for; everything else is
172
+ // only decoded once the gate has passed.
173
+ let request = cmd === "Settings" && handler ? this.decodeRequest(req) : undefined;
174
+ // Every command except what must work on an unprovisioned device (Provision itself, and the first-run
175
+ // Settings lookups real clients make before provisioning completes) requires the device to be provisioned AND
176
+ // to present the policy key it acknowledged ([MS-ASPROV]: `X-MS-PolicyKey`, or the `PolicyKey` query value).
177
+ // A missing or stale key - e.g. one from before an admin-requested remote wipe, which clears the stored key -
178
+ // is sent back through Provision with the same 449 rather than being served.
179
+ if (!this.exemptFromProvisioning(cmd, request)) {
180
+ const presentedKey = firstQueryValue(req.headers["x-ms-policykey"]) ?? policyKey;
181
+ if (!deviceSyncState.provisioned || !deviceSyncState.policyKey || presentedKey !== deviceSyncState.policyKey) {
182
+ res.status(HTTP_STATUS_RETRY_WITH).send();
183
+ return;
184
+ }
185
+ }
143
186
  if (!handler) {
144
187
  res.status(501).send();
145
188
  return;
146
189
  }
147
- const request = req.rawBody && req.rawBody.length > 0 ? new WbxmlDecoder().decode(req.rawBody) : undefined;
190
+ if (cmd !== "Settings") {
191
+ request = this.decodeRequest(req);
192
+ }
148
193
  const response = await handler.handle({
149
194
  user,
150
195
  mailboxUid,
151
196
  deviceId,
152
197
  deviceType,
153
- policyKey,
198
+ policyKey: firstQueryValue(req.headers["x-ms-policykey"]) ?? policyKey,
154
199
  deviceSyncState,
155
200
  deviceSyncStateRepo: this.deviceSyncStateRepo,
156
201
  query: req.query,
157
202
  request,
158
203
  req,
204
+ res,
159
205
  });
160
- await persistDeviceSyncState(deviceSyncState, this.deviceSyncStateRepo, { lastSyncAt: new Date() });
206
+ // Bookkeeping only - the command already ran, so failing to record the timestamp must never turn a
207
+ // successful response into an error.
208
+ try {
209
+ await persistDeviceSyncState(deviceSyncState, this.deviceSyncStateRepo, { lastSyncAt: new Date() });
210
+ }
211
+ catch (err) {
212
+ this.logger?.warn(`BaseEasRoute: failed to record lastSyncAt for device ${deviceId}: ${err?.message}`);
213
+ }
161
214
  if (!response) {
162
215
  res.status(200).send();
163
216
  return;
@@ -168,6 +221,25 @@ export class BaseEasRoute {
168
221
  .status(200)
169
222
  .send(buffer);
170
223
  }
224
+ /** Decodes the WBXML request body, if any (malformed or over the decoder's element limits -> 400). */
225
+ decodeRequest(req) {
226
+ try {
227
+ return req.rawBody && req.rawBody.length > 0 ? new WbxmlDecoder().decode(req.rawBody) : undefined;
228
+ }
229
+ catch (err) {
230
+ if (err instanceof WbxmlDecodeError) {
231
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, `Malformed WBXML request: ${err.message}`);
232
+ }
233
+ throw err;
234
+ }
235
+ }
236
+ /** `Provision`, and a `Settings` request whose every element is one `SETTINGS_WITHOUT_PROVISIONING` allows. */
237
+ exemptFromProvisioning(cmd, request) {
238
+ if (cmd === "Provision") {
239
+ return true;
240
+ }
241
+ return cmd === "Settings" && (request?.children ?? []).every((child) => SETTINGS_WITHOUT_PROVISIONING.has(child.tag));
242
+ }
171
243
  async findOrCreateDeviceSyncState(mailboxUid, deviceId, deviceType) {
172
244
  const existing = await this.deviceSyncStateRepo.find({ mailboxUid, deviceId }, { ignoreACL: true, limit: 1 });
173
245
  if (existing[0]) {
@@ -183,6 +255,10 @@ export class BaseEasRoute {
183
255
  return await this.deviceSyncStateRepo.create(instance, { ignoreACL: true });
184
256
  }
185
257
  }
258
+ __decorate([
259
+ Config("mail:eas:max_request_bytes", DEFAULT_MAX_REQUEST_BYTES),
260
+ __metadata("design:type", Number)
261
+ ], BaseEasRoute.prototype, "maxRequestBytes", void 0);
186
262
  __decorate([
187
263
  Logger,
188
264
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"BaseEasRoute.js","sourceRoot":"","sources":["../../src/BaseEasRoute.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseEasRoute.js","sourceRoot":"","sources":["../../src/BaseEasRoute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { isNonOwnerAccess, recordAuditLog } from "@rapidmx/restapi";
2
+ /**
3
+ * Audit entries for ActiveSync access to mail in a mailbox the caller doesn't own - an administrator reaching in
4
+ * through a trusted role, or a delegate working in a shared folder. It uses restapi's `recordAuditLog()` and
5
+ * `isNonOwnerAccess()`, as `BaseMessageRoute` does: REST records `MESSAGE_CONTENT_ACCESSED` for a non-owner content
6
+ * read and `MESSAGE_DELETE` for a delete.
7
+ *
8
+ * Built once per request. The caller's own mailbox (`ctx.mailboxUid`, resolved by `ownerUserUid`) is never audited
9
+ * and needs no lookup. Any other mailbox is loaded once. A mailbox that can't be loaded counts as non-owner, the
10
+ * defensive reading restapi's `content()` uses. Recording never throws: an audit failure is logged and the command
11
+ * goes on, like restapi's own best-effort persistence.
12
+ *
13
+ * Every entry's `details` carries `protocol: "ActiveSync"`, the command and the `DeviceId`.
14
+ */
15
+ export class EasAuditLog {
16
+ constructor(deps, ctx, command) {
17
+ this.deps = deps;
18
+ this.ctx = ctx;
19
+ this.command = command;
20
+ this.nonOwner = new Map();
21
+ }
22
+ /** Whether reading `mailboxUid`'s content is a non-owner access for this request's caller. */
23
+ isNonOwner(mailboxUid) {
24
+ if (mailboxUid === this.ctx.mailboxUid) {
25
+ return Promise.resolve(false);
26
+ }
27
+ let pending = this.nonOwner.get(mailboxUid);
28
+ if (!pending) {
29
+ pending = this.deps.mailboxRepo
30
+ .findOne(mailboxUid, { ignoreACL: true })
31
+ .then((mailbox) => !mailbox || isNonOwnerAccess(mailbox, this.ctx.user))
32
+ .catch(() => true);
33
+ this.nonOwner.set(mailboxUid, pending);
34
+ }
35
+ return pending;
36
+ }
37
+ /** Records `entry` when its mailbox isn't the caller's own. */
38
+ async record(entry) {
39
+ try {
40
+ if (!(await this.isNonOwner(entry.mailboxUid))) {
41
+ return;
42
+ }
43
+ await recordAuditLog(this.deps.objectFactory, this.deps.auditLogClass, { config: this.deps.config, req: this.ctx.req, user: this.ctx.user, logger: this.deps.logger }, {
44
+ action: entry.action,
45
+ targetType: entry.targetType,
46
+ targetUid: entry.targetUid,
47
+ mailboxUid: entry.mailboxUid,
48
+ details: { protocol: "ActiveSync", command: this.command, deviceId: this.ctx.deviceId, ...entry.details },
49
+ });
50
+ }
51
+ catch (err) {
52
+ this.deps.logger?.warn(`${this.command}: failed to record audit entry ${entry.action} ${entry.targetType}:${entry.targetUid}: ${err?.message}`);
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=EasAuditLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EasAuditLog.js","sourceRoot":"","sources":["../../src/EasAuditLog.ts"],"names":[],"mappings":"AAMA,OAAO,EAAe,gBAAgB,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAwB/F;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,WAAW;IAGpB,YACqB,IAA0B,EAC1B,GAAsB,EACtB,OAAe;QAFf,SAAI,GAAJ,IAAI,CAAsB;QAC1B,QAAG,GAAH,GAAG,CAAmB;QACtB,YAAO,GAAP,OAAO,CAAQ;QALnB,aAAQ,GAAkC,IAAI,GAAG,EAAE,CAAC;IAMlE,CAAC;IAEJ,8FAA8F;IACvF,UAAU,CAAC,UAAkB;QAChC,IAAI,UAAU,KAAK,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;YACrC,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,OAAO,GAAiC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1E,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW;iBAC1B,OAAO,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;iBACxC,IAAI,CAAC,CAAC,OAA4B,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;iBAC5F,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,+DAA+D;IACxD,KAAK,CAAC,MAAM,CAAC,KAAoB;QACpC,IAAI,CAAC;YACD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO;YACX,CAAC;YACD,MAAM,cAAc,CAChB,IAAI,CAAC,IAAI,CAAC,aAAa,EACvB,IAAI,CAAC,IAAI,CAAC,aAAa,EACvB,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAC9F;gBACI,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE;aAC5G,CACJ,CAAC;QACN,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,kCAAkC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACpJ,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,199 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import * as crypto from "crypto";
6
+ import { createClient } from "redis";
7
+ /** Deletes the lease key only while it still carries this holder's token, so an expired-and-retaken lease survives. */
8
+ const RELEASE_SCRIPT = 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end';
9
+ /** Extends the lease key's expiry only while it still carries this holder's token. */
10
+ const RENEW_SCRIPT = 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end';
11
+ /** Default `LeaseOptions.redisTimeoutMs`. */
12
+ const DEFAULT_REDIS_TIMEOUT_MS = 2000;
13
+ /** Reconnect attempts after which a lost Redis connection gives up (and a later lease creates a fresh client). */
14
+ const MAX_RECONNECT_ATTEMPTS = 3;
15
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
16
+ /** Sentinel a timed-out Redis call resolves with. */
17
+ const TIMED_OUT = Symbol("timed out");
18
+ /** Resolves with `promise`'s value, or `TIMED_OUT` after `ms` (a rejection still rejects). */
19
+ async function withTimeout(promise, ms) {
20
+ let timer;
21
+ try {
22
+ return await Promise.race([promise, new Promise((resolve) => (timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, ms))))]);
23
+ }
24
+ finally {
25
+ clearTimeout(timer);
26
+ }
27
+ }
28
+ /**
29
+ * A short exclusive lease on one key - `SyncCommand` holds one per (mailbox, device, folder) for the duration of a
30
+ * collection's round, so two concurrent `Sync`s of the same collection (a client retrying while its first request is
31
+ * still running, or two server copies) can't both read the same state, apply their commands and overwrite each
32
+ * other's result.
33
+ *
34
+ * Always taken in-process first (a per-key promise chain), then - when a Redis URL is configured - as a `SET NX PX`
35
+ * key shared by every server copy, released with a token check and renewed (token-checked `PEXPIRE`) every
36
+ * `ttlMs / 3` while held, so a round that outlasts `ttlMs` doesn't lose it to another copy.
37
+ *
38
+ * **Redis being unreachable fails open** to the in-process lease alone rather than refusing - or hanging - every
39
+ * `Sync`: the client is created with `disableOfflineQueue` (a command fails at once while disconnected instead of
40
+ * waiting in a queue) and a bounded `reconnectStrategy` (so `connect()` rejects instead of retrying forever), and
41
+ * every connect/`SET` is additionally raced against `redisTimeoutMs`. The connect and the first `SET` get the whole
42
+ * `redisTimeoutMs` even when the in-process wait already used up `waitMs` - otherwise a healthy Redis would get no time
43
+ * at all and the lease would fail open for nothing; only the polling `SET`s after Redis has answered are cut short by
44
+ * the deadline, which then means another copy still holds the key (`undefined`). A `SET` that times out but lands later
45
+ * is released again straight away. A client whose reconnects gave up is replaced on the next lease.
46
+ *
47
+ * **Release never hangs**: the token-checked release is raced against `redisTimeoutMs` too (the key expires on its
48
+ * own), and the in-process lease is freed whatever happens to it.
49
+ *
50
+ * @author Jean-Philippe Steinmetz
51
+ */
52
+ export class EasCollectionLease {
53
+ /** Forgets all process-wide state. Intended for tests. */
54
+ static resetSharedState() {
55
+ EasCollectionLease.local.clear();
56
+ EasCollectionLease.clients.clear();
57
+ }
58
+ static getClient(url, connectTimeoutMs) {
59
+ let pending = EasCollectionLease.clients.get(url);
60
+ if (!pending) {
61
+ const client = createClient({
62
+ url,
63
+ disableOfflineQueue: true,
64
+ socket: {
65
+ connectTimeout: connectTimeoutMs,
66
+ reconnectStrategy: (retries) => retries >= MAX_RECONNECT_ATTEMPTS ? new Error("Redis unreachable") : Math.min(100 * 2 ** retries, 1000),
67
+ },
68
+ });
69
+ // An unhandled `error` event would crash the process.
70
+ client.on("error", () => undefined);
71
+ const connecting = client.connect().then(() => client);
72
+ connecting.catch(() => EasCollectionLease.forgetClient(url, connecting));
73
+ EasCollectionLease.clients.set(url, connecting);
74
+ pending = connecting;
75
+ }
76
+ return pending;
77
+ }
78
+ static forgetClient(url, pending) {
79
+ if (EasCollectionLease.clients.get(url) === pending) {
80
+ EasCollectionLease.clients.delete(url);
81
+ }
82
+ }
83
+ /** Waits for `key`'s in-process holder (if any) to release, up to `deadline`, then takes it over with `held` - the
84
+ * check and the take-over happen in the same tick, so two waiters can never both find the key free. `false` on
85
+ * timeout. */
86
+ static async takeLocal(key, held, deadline) {
87
+ for (;;) {
88
+ const holder = EasCollectionLease.local.get(key);
89
+ if (!holder) {
90
+ EasCollectionLease.local.set(key, held);
91
+ return true;
92
+ }
93
+ const remaining = deadline - Date.now();
94
+ if (remaining <= 0) {
95
+ return false;
96
+ }
97
+ let timer;
98
+ await Promise.race([holder, new Promise((resolve) => (timer = setTimeout(resolve, remaining)))]);
99
+ clearTimeout(timer);
100
+ }
101
+ }
102
+ /**
103
+ * Acquires the lease on `key`, waiting up to `options.waitMs` for a current holder. Resolves with the release
104
+ * function, or `undefined` when the lease couldn't be acquired in time.
105
+ */
106
+ static async acquire(key, options) {
107
+ const deadline = Date.now() + options.waitMs;
108
+ let resolveLocal;
109
+ const held = new Promise((resolve) => (resolveLocal = resolve));
110
+ if (!(await EasCollectionLease.takeLocal(key, held, deadline))) {
111
+ return undefined;
112
+ }
113
+ const releaseLocal = () => {
114
+ if (EasCollectionLease.local.get(key) === held) {
115
+ EasCollectionLease.local.delete(key);
116
+ }
117
+ resolveLocal();
118
+ };
119
+ const failOpen = async () => releaseLocal();
120
+ if (!options.redisUrl) {
121
+ return failOpen;
122
+ }
123
+ const url = options.redisUrl;
124
+ const redisTimeoutMs = options.redisTimeoutMs ?? DEFAULT_REDIS_TIMEOUT_MS;
125
+ // A polling SET never waits past the deadline for Redis; with no time left at all, it still gets the current tick.
126
+ const pollTimeout = () => Math.min(redisTimeoutMs, Math.max(0, deadline - Date.now()));
127
+ const redisKey = `eas:lease:${key}`;
128
+ const token = crypto.randomUUID();
129
+ const releaseRedis = async (client) => {
130
+ try {
131
+ await client.eval(RELEASE_SCRIPT, { keys: [redisKey], arguments: [token] });
132
+ }
133
+ catch {
134
+ // The key expires on its own.
135
+ }
136
+ };
137
+ let client;
138
+ try {
139
+ const pending = EasCollectionLease.getClient(url, redisTimeoutMs);
140
+ const connected = await withTimeout(pending, redisTimeoutMs);
141
+ if (connected === TIMED_OUT) {
142
+ return failOpen;
143
+ }
144
+ client = connected;
145
+ if (client.isOpen === false) {
146
+ // Its reconnects gave up: the next lease starts a fresh client.
147
+ EasCollectionLease.forgetClient(url, pending);
148
+ return failOpen;
149
+ }
150
+ let answered = false;
151
+ for (;;) {
152
+ const setting = client.set(redisKey, token, { condition: "NX", expiration: { type: "PX", value: options.ttlMs } });
153
+ const reply = await withTimeout(setting, answered ? pollTimeout() : redisTimeoutMs);
154
+ if (reply === TIMED_OUT) {
155
+ // Give back the key should the SET still land.
156
+ setting.then((late) => (late === "OK" ? releaseRedis(client) : undefined)).catch(() => undefined);
157
+ if (answered && Date.now() >= deadline) {
158
+ // Redis is up and another copy holds the key - only the wait ran out.
159
+ releaseLocal();
160
+ return undefined;
161
+ }
162
+ return failOpen;
163
+ }
164
+ answered = true;
165
+ if (reply === "OK") {
166
+ break;
167
+ }
168
+ if (Date.now() >= deadline) {
169
+ releaseLocal();
170
+ return undefined;
171
+ }
172
+ await sleep(Math.min(options.pollMs ?? 100, Math.max(1, deadline - Date.now())));
173
+ }
174
+ }
175
+ catch {
176
+ // Redis unreachable: fail open to the in-process lease.
177
+ return failOpen;
178
+ }
179
+ const renewal = setInterval(() => {
180
+ client.eval(RENEW_SCRIPT, { keys: [redisKey], arguments: [token, String(options.ttlMs)] }).catch(() => undefined);
181
+ }, Math.max(1, Math.floor(options.ttlMs / 3)));
182
+ renewal.unref?.();
183
+ return async () => {
184
+ clearInterval(renewal);
185
+ try {
186
+ // An unresponsive (but connected) Redis must not hold this collection on this server copy.
187
+ await withTimeout(releaseRedis(client), redisTimeoutMs);
188
+ }
189
+ finally {
190
+ releaseLocal();
191
+ }
192
+ };
193
+ }
194
+ }
195
+ /** The in-process holder of each key: resolves once that holder releases. */
196
+ EasCollectionLease.local = new Map();
197
+ /** Shared command clients, keyed by Redis URL. */
198
+ EasCollectionLease.clients = new Map();
199
+ //# sourceMappingURL=EasCollectionLease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EasCollectionLease.js","sourceRoot":"","sources":["../../src/EasCollectionLease.ts"],"names":[],"mappings":"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"}