@proteinjs/db 1.35.1 → 1.36.0

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 (160) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/generated/index.d.ts.map +1 -1
  3. package/dist/generated/index.js +7 -1
  4. package/dist/generated/index.js.map +1 -1
  5. package/dist/generated/test/index.d.ts.map +1 -1
  6. package/dist/generated/test/index.js +7 -1
  7. package/dist/generated/test/index.js.map +1 -1
  8. package/dist/index.d.ts +15 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +15 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/src/Db.d.ts +25 -0
  13. package/dist/src/Db.d.ts.map +1 -1
  14. package/dist/src/Db.js +219 -22
  15. package/dist/src/Db.js.map +1 -1
  16. package/dist/src/Record.d.ts +7 -1
  17. package/dist/src/Record.d.ts.map +1 -1
  18. package/dist/src/Record.js +56 -21
  19. package/dist/src/Record.js.map +1 -1
  20. package/dist/src/Table.d.ts +82 -1
  21. package/dist/src/Table.d.ts.map +1 -1
  22. package/dist/src/Table.js +24 -1
  23. package/dist/src/Table.js.map +1 -1
  24. package/dist/src/TableQueryTransformProvider.d.ts +17 -0
  25. package/dist/src/TableQueryTransformProvider.d.ts.map +1 -0
  26. package/dist/src/TableQueryTransformProvider.js +34 -0
  27. package/dist/src/TableQueryTransformProvider.js.map +1 -0
  28. package/dist/src/encryption/Base64Url.d.ts +12 -0
  29. package/dist/src/encryption/Base64Url.d.ts.map +1 -0
  30. package/dist/src/encryption/Base64Url.js +21 -0
  31. package/dist/src/encryption/Base64Url.js.map +1 -0
  32. package/dist/src/encryption/DataEncryptionKeyTable.d.ts +33 -0
  33. package/dist/src/encryption/DataEncryptionKeyTable.d.ts.map +1 -0
  34. package/dist/src/encryption/DataEncryptionKeyTable.js +45 -0
  35. package/dist/src/encryption/DataEncryptionKeyTable.js.map +1 -0
  36. package/dist/src/encryption/DataKeyStore.d.ts +84 -0
  37. package/dist/src/encryption/DataKeyStore.d.ts.map +1 -0
  38. package/dist/src/encryption/DataKeyStore.js +481 -0
  39. package/dist/src/encryption/DataKeyStore.js.map +1 -0
  40. package/dist/src/encryption/DbEncryptionConfig.d.ts +62 -0
  41. package/dist/src/encryption/DbEncryptionConfig.d.ts.map +1 -0
  42. package/dist/src/encryption/DbEncryptionConfig.js +67 -0
  43. package/dist/src/encryption/DbEncryptionConfig.js.map +1 -0
  44. package/dist/src/encryption/EncryptedColumnQueryError.d.ts +10 -0
  45. package/dist/src/encryption/EncryptedColumnQueryError.d.ts.map +1 -0
  46. package/dist/src/encryption/EncryptedColumnQueryError.js +38 -0
  47. package/dist/src/encryption/EncryptedColumnQueryError.js.map +1 -0
  48. package/dist/src/encryption/EncryptedColumnQueryTransform.d.ts +52 -0
  49. package/dist/src/encryption/EncryptedColumnQueryTransform.d.ts.map +1 -0
  50. package/dist/src/encryption/EncryptedColumnQueryTransform.js +423 -0
  51. package/dist/src/encryption/EncryptedColumnQueryTransform.js.map +1 -0
  52. package/dist/src/encryption/EncryptedColumns.d.ts +69 -0
  53. package/dist/src/encryption/EncryptedColumns.d.ts.map +1 -0
  54. package/dist/src/encryption/EncryptedColumns.js +286 -0
  55. package/dist/src/encryption/EncryptedColumns.js.map +1 -0
  56. package/dist/src/encryption/EncryptionDerivedTableRegistry.d.ts +20 -0
  57. package/dist/src/encryption/EncryptionDerivedTableRegistry.d.ts.map +1 -0
  58. package/dist/src/encryption/EncryptionDerivedTableRegistry.js +36 -0
  59. package/dist/src/encryption/EncryptionDerivedTableRegistry.js.map +1 -0
  60. package/dist/src/encryption/EncryptionEnvelope.d.ts +25 -0
  61. package/dist/src/encryption/EncryptionEnvelope.d.ts.map +1 -0
  62. package/dist/src/encryption/EncryptionEnvelope.js +59 -0
  63. package/dist/src/encryption/EncryptionEnvelope.js.map +1 -0
  64. package/dist/src/encryption/EncryptionLifecycleWalker.d.ts +65 -0
  65. package/dist/src/encryption/EncryptionLifecycleWalker.d.ts.map +1 -0
  66. package/dist/src/encryption/EncryptionLifecycleWalker.js +343 -0
  67. package/dist/src/encryption/EncryptionLifecycleWalker.js.map +1 -0
  68. package/dist/src/encryption/EncryptionRecordHooks.d.ts +47 -0
  69. package/dist/src/encryption/EncryptionRecordHooks.d.ts.map +1 -0
  70. package/dist/src/encryption/EncryptionRecordHooks.js +229 -0
  71. package/dist/src/encryption/EncryptionRecordHooks.js.map +1 -0
  72. package/dist/src/encryption/EncryptionTokenMaintenance.d.ts +34 -0
  73. package/dist/src/encryption/EncryptionTokenMaintenance.d.ts.map +1 -0
  74. package/dist/src/encryption/EncryptionTokenMaintenance.js +211 -0
  75. package/dist/src/encryption/EncryptionTokenMaintenance.js.map +1 -0
  76. package/dist/src/encryption/InMemoryMasterKeyProvider.d.ts +20 -0
  77. package/dist/src/encryption/InMemoryMasterKeyProvider.d.ts.map +1 -0
  78. package/dist/src/encryption/InMemoryMasterKeyProvider.js +87 -0
  79. package/dist/src/encryption/InMemoryMasterKeyProvider.js.map +1 -0
  80. package/dist/src/encryption/MasterKeyProvider.d.ts +27 -0
  81. package/dist/src/encryption/MasterKeyProvider.d.ts.map +1 -0
  82. package/dist/src/encryption/MasterKeyProvider.js +3 -0
  83. package/dist/src/encryption/MasterKeyProvider.js.map +1 -0
  84. package/dist/src/encryption/SearchTokenizer.d.ts +49 -0
  85. package/dist/src/encryption/SearchTokenizer.d.ts.map +1 -0
  86. package/dist/src/encryption/SearchTokenizer.js +106 -0
  87. package/dist/src/encryption/SearchTokenizer.js.map +1 -0
  88. package/dist/src/schema/TableManager.d.ts +10 -0
  89. package/dist/src/schema/TableManager.d.ts.map +1 -1
  90. package/dist/src/schema/TableManager.js +62 -5
  91. package/dist/src/schema/TableManager.js.map +1 -1
  92. package/dist/src/tables/MigrationTable.d.ts +2 -2
  93. package/dist/test/EncryptedColumnsSchema.test.d.ts +2 -0
  94. package/dist/test/EncryptedColumnsSchema.test.d.ts.map +1 -0
  95. package/dist/test/EncryptedColumnsSchema.test.js +151 -0
  96. package/dist/test/EncryptedColumnsSchema.test.js.map +1 -0
  97. package/dist/test/EncryptionEnvelope.test.d.ts +2 -0
  98. package/dist/test/EncryptionEnvelope.test.d.ts.map +1 -0
  99. package/dist/test/EncryptionEnvelope.test.js +109 -0
  100. package/dist/test/EncryptionEnvelope.test.js.map +1 -0
  101. package/dist/test/EncryptionQueryContract.test.d.ts +2 -0
  102. package/dist/test/EncryptionQueryContract.test.d.ts.map +1 -0
  103. package/dist/test/EncryptionQueryContract.test.js +399 -0
  104. package/dist/test/EncryptionQueryContract.test.js.map +1 -0
  105. package/dist/test/SearchTokenizer.test.d.ts +2 -0
  106. package/dist/test/SearchTokenizer.test.d.ts.map +1 -0
  107. package/dist/test/SearchTokenizer.test.js +64 -0
  108. package/dist/test/SearchTokenizer.test.js.map +1 -0
  109. package/dist/test/reusable/CascadeDeleteTests.js +2 -2
  110. package/dist/test/reusable/CascadeDeleteTests.js.map +1 -1
  111. package/dist/test/reusable/ColumnTypesTests.js +2 -2
  112. package/dist/test/reusable/ColumnTypesTests.js.map +1 -1
  113. package/dist/test/reusable/CrudTests.js +2 -2
  114. package/dist/test/reusable/CrudTests.js.map +1 -1
  115. package/dist/test/reusable/DynamicReferenceColumnTests.js +2 -2
  116. package/dist/test/reusable/DynamicReferenceColumnTests.js.map +1 -1
  117. package/dist/test/reusable/PreloadReferencesTests.js +2 -2
  118. package/dist/test/reusable/PreloadReferencesTests.js.map +1 -1
  119. package/dist/test/reusable/RecordIteratorTests.js +2 -2
  120. package/dist/test/reusable/RecordIteratorTests.js.map +1 -1
  121. package/dist/test/reusable/TableManagerTests.js +2 -2
  122. package/dist/test/reusable/TableManagerTests.js.map +1 -1
  123. package/dist/test/reusable/TransactionTests.js +2 -2
  124. package/dist/test/reusable/TransactionTests.js.map +1 -1
  125. package/generated/index.ts +7 -1
  126. package/generated/test/index.ts +7 -1
  127. package/index.ts +16 -0
  128. package/package.json +3 -3
  129. package/src/Db.ts +137 -12
  130. package/src/Record.ts +21 -4
  131. package/src/Table.ts +109 -2
  132. package/src/TableQueryTransformProvider.ts +31 -0
  133. package/src/encryption/Base64Url.ts +14 -0
  134. package/src/encryption/DataEncryptionKeyTable.ts +40 -0
  135. package/src/encryption/DataKeyStore.ts +305 -0
  136. package/src/encryption/DbEncryptionConfig.ts +92 -0
  137. package/src/encryption/EncryptedColumnQueryError.ts +15 -0
  138. package/src/encryption/EncryptedColumnQueryTransform.ts +383 -0
  139. package/src/encryption/EncryptedColumns.ts +318 -0
  140. package/src/encryption/EncryptionDerivedTableRegistry.ts +34 -0
  141. package/src/encryption/EncryptionEnvelope.ts +60 -0
  142. package/src/encryption/EncryptionLifecycleWalker.ts +243 -0
  143. package/src/encryption/EncryptionRecordHooks.ts +167 -0
  144. package/src/encryption/EncryptionTokenMaintenance.ts +123 -0
  145. package/src/encryption/InMemoryMasterKeyProvider.ts +42 -0
  146. package/src/encryption/MasterKeyProvider.ts +26 -0
  147. package/src/encryption/SearchTokenizer.ts +103 -0
  148. package/src/schema/TableManager.ts +43 -1
  149. package/test/EncryptedColumnsSchema.test.ts +172 -0
  150. package/test/EncryptionEnvelope.test.ts +66 -0
  151. package/test/EncryptionQueryContract.test.ts +195 -0
  152. package/test/SearchTokenizer.test.ts +67 -0
  153. package/test/reusable/CascadeDeleteTests.ts +2 -2
  154. package/test/reusable/ColumnTypesTests.ts +2 -2
  155. package/test/reusable/CrudTests.ts +2 -2
  156. package/test/reusable/DynamicReferenceColumnTests.ts +2 -2
  157. package/test/reusable/PreloadReferencesTests.ts +2 -2
  158. package/test/reusable/RecordIteratorTests.ts +2 -2
  159. package/test/reusable/TableManagerTests.ts +2 -2
  160. package/test/reusable/TransactionTests.ts +2 -2
@@ -0,0 +1,343 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __generator = (this && this.__generator) || function (thisArg, body) {
46
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48
+ function verb(n) { return function (v) { return step([n, v]); }; }
49
+ function step(op) {
50
+ if (f) throw new TypeError("Generator is already executing.");
51
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
52
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
53
+ if (y = 0, t) op = [op[0] & 2, t.value];
54
+ switch (op[0]) {
55
+ case 0: case 1: t = op; break;
56
+ case 4: _.label++; return { value: op[1], done: false };
57
+ case 5: _.label++; y = op[1]; op = [0]; continue;
58
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
59
+ default:
60
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64
+ if (t[2]) _.ops.pop();
65
+ _.trys.pop(); continue;
66
+ }
67
+ op = body.call(thisArg, _);
68
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70
+ }
71
+ };
72
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
73
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
74
+ if (ar || !(i in from)) {
75
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
76
+ ar[i] = from[i];
77
+ }
78
+ }
79
+ return to.concat(ar || Array.prototype.slice.call(from));
80
+ };
81
+ Object.defineProperty(exports, "__esModule", { value: true });
82
+ exports.EncryptionLifecycleWalker = void 0;
83
+ var db_query_1 = require("@proteinjs/db-query");
84
+ var logger_1 = require("@proteinjs/logger");
85
+ var EncryptedColumns_1 = require("./EncryptedColumns");
86
+ var EncryptionEnvelope_1 = require("./EncryptionEnvelope");
87
+ var DataKeyStore_1 = require("./DataKeyStore");
88
+ /**
89
+ * The reusable ONLINE backfill behind every encrypted-column config transition (and key
90
+ * rotation) — the migration-runner pattern: a server-side, id-ordered, windowed table walk
91
+ * that rewrites pending rows THROUGH the normal write seam (so encryption, companions, and
92
+ * search tokens all derive exactly as a live write would).
93
+ *
94
+ * Idempotent and resumable by construction: pending-ness is detected from the stored value
95
+ * itself (envelope or not, version current or not), so a re-run — after a crash, or just to
96
+ * be sure — skips completed rows and continues. Rows written by live traffic mid-walk are
97
+ * already in the new shape and skip too. Run it from a `Migration.run()` for deploy-coupled
98
+ * transitions.
99
+ */
100
+ var EncryptionLifecycleWalker = /** @class */ (function () {
101
+ function EncryptionLifecycleWalker() {
102
+ this.logger = new logger_1.Logger({ name: this.constructor.name });
103
+ this.encryptedColumns = new EncryptedColumns_1.EncryptedColumns();
104
+ this.envelope = new EncryptionEnvelope_1.EncryptionEnvelope();
105
+ }
106
+ EncryptionLifecycleWalker.prototype.walkTable = function (table, mode, options) {
107
+ var _a, _b, _c;
108
+ if (options === void 0) { options = {}; }
109
+ return __awaiter(this, void 0, void 0, function () {
110
+ var props, _d, Db, getDbAsSystem, dbDriver, db, windowSize, columnNames, activeVersionByOwner, summary, cursor, rawRows, _i, rawRows_1, rawRow, pendingProps, _e, columnNames_1, _f, prop, columnName;
111
+ return __generator(this, function (_g) {
112
+ switch (_g.label) {
113
+ case 0:
114
+ this.encryptedColumns.ensureSchema(table);
115
+ props = this.resolveProps(table, mode, options);
116
+ return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require('../Db')); })];
117
+ case 1:
118
+ _d = _g.sent(), Db = _d.Db, getDbAsSystem = _d.getDbAsSystem;
119
+ dbDriver = (_a = options.dbDriver) !== null && _a !== void 0 ? _a : Db.getDefaultDbDriver();
120
+ db = (_b = options.db) !== null && _b !== void 0 ? _b : getDbAsSystem();
121
+ windowSize = (_c = options.windowSize) !== null && _c !== void 0 ? _c : EncryptionLifecycleWalker.DEFAULT_WINDOW_SIZE;
122
+ columnNames = props.map(function (prop) { return ({
123
+ prop: prop,
124
+ columnName: table.columns[prop].name,
125
+ }); });
126
+ activeVersionByOwner = new Map();
127
+ summary = { scanned: 0, rewritten: 0 };
128
+ cursor = options.startAfterId;
129
+ _g.label = 2;
130
+ case 2:
131
+ if (!true) return [3 /*break*/, 12];
132
+ return [4 /*yield*/, this.rawWindow(table, dbDriver, columnNames, cursor, windowSize)];
133
+ case 3:
134
+ rawRows = _g.sent();
135
+ if (rawRows.length === 0) {
136
+ return [3 /*break*/, 12];
137
+ }
138
+ _i = 0, rawRows_1 = rawRows;
139
+ _g.label = 4;
140
+ case 4:
141
+ if (!(_i < rawRows_1.length)) return [3 /*break*/, 11];
142
+ rawRow = rawRows_1[_i];
143
+ summary.scanned++;
144
+ pendingProps = [];
145
+ _e = 0, columnNames_1 = columnNames;
146
+ _g.label = 5;
147
+ case 5:
148
+ if (!(_e < columnNames_1.length)) return [3 /*break*/, 8];
149
+ _f = columnNames_1[_e], prop = _f.prop, columnName = _f.columnName;
150
+ return [4 /*yield*/, this.isPending(mode, rawRow[columnName], activeVersionByOwner)];
151
+ case 6:
152
+ if (_g.sent()) {
153
+ pendingProps.push(prop);
154
+ }
155
+ _g.label = 7;
156
+ case 7:
157
+ _e++;
158
+ return [3 /*break*/, 5];
159
+ case 8:
160
+ if (pendingProps.length === 0) {
161
+ return [3 /*break*/, 10];
162
+ }
163
+ return [4 /*yield*/, this.rewriteRow(table, db, rawRow['id'], pendingProps)];
164
+ case 9:
165
+ _g.sent();
166
+ summary.rewritten++;
167
+ _g.label = 10;
168
+ case 10:
169
+ _i++;
170
+ return [3 /*break*/, 4];
171
+ case 11:
172
+ cursor = rawRows[rawRows.length - 1]['id'];
173
+ if (rawRows.length < windowSize) {
174
+ return [3 /*break*/, 12];
175
+ }
176
+ return [3 /*break*/, 2];
177
+ case 12:
178
+ if (!(mode === 'decrypt')) return [3 /*break*/, 14];
179
+ return [4 /*yield*/, this.sweepTokenRows(table, db, columnNames.map(function (_a) {
180
+ var columnName = _a.columnName;
181
+ return columnName;
182
+ }))];
183
+ case 13:
184
+ _g.sent();
185
+ _g.label = 14;
186
+ case 14:
187
+ this.logger.info({
188
+ message: "Encryption lifecycle walk finished",
189
+ obj: __assign({ table: table.name, mode: mode }, summary),
190
+ });
191
+ return [2 /*return*/, summary];
192
+ }
193
+ });
194
+ });
195
+ };
196
+ EncryptionLifecycleWalker.prototype.isPending = function (mode, rawValue, activeVersionByOwner) {
197
+ return __awaiter(this, void 0, void 0, function () {
198
+ var parsed, _a, activeVersion;
199
+ return __generator(this, function (_b) {
200
+ switch (_b.label) {
201
+ case 0:
202
+ if (rawValue === null || typeof rawValue === 'undefined') {
203
+ return [2 /*return*/, false];
204
+ }
205
+ parsed = this.envelope.parse(rawValue);
206
+ _a = mode;
207
+ switch (_a) {
208
+ case 'encrypt': return [3 /*break*/, 1];
209
+ case 'retokenize': return [3 /*break*/, 2];
210
+ case 'decrypt': return [3 /*break*/, 3];
211
+ case 'rotate-keys': return [3 /*break*/, 4];
212
+ }
213
+ return [3 /*break*/, 7];
214
+ case 1: return [2 /*return*/, !parsed];
215
+ case 2: return [2 /*return*/, true];
216
+ case 3: return [2 /*return*/, !!parsed];
217
+ case 4:
218
+ if (!parsed) {
219
+ return [2 /*return*/, true]; // plaintext straggler — encrypt under the current key
220
+ }
221
+ activeVersion = activeVersionByOwner.get(parsed.owner);
222
+ if (!(typeof activeVersion === 'undefined')) return [3 /*break*/, 6];
223
+ return [4 /*yield*/, new DataKeyStore_1.DataKeyStore().getWriteKey(parsed.owner)];
224
+ case 5:
225
+ activeVersion = (_b.sent()).version;
226
+ activeVersionByOwner.set(parsed.owner, activeVersion);
227
+ _b.label = 6;
228
+ case 6: return [2 /*return*/, parsed.version < activeVersion];
229
+ case 7: return [2 /*return*/];
230
+ }
231
+ });
232
+ });
233
+ };
234
+ /**
235
+ * Rewrite one row through the normal seam: read it (the deserialize hook decrypts — or
236
+ * passes plaintext through, the adoption path), write the pending columns back (the
237
+ * serialize hook re-derives ciphertext, companions, and tokens under the current config).
238
+ */
239
+ EncryptionLifecycleWalker.prototype.rewriteRow = function (table, db, id, pendingProps) {
240
+ return __awaiter(this, void 0, void 0, function () {
241
+ var row, payload, _i, pendingProps_1, prop;
242
+ return __generator(this, function (_a) {
243
+ switch (_a.label) {
244
+ case 0: return [4 /*yield*/, db.get(table, { id: id })];
245
+ case 1:
246
+ row = _a.sent();
247
+ if (!row) {
248
+ return [2 /*return*/]; // deleted mid-walk
249
+ }
250
+ payload = { id: id };
251
+ for (_i = 0, pendingProps_1 = pendingProps; _i < pendingProps_1.length; _i++) {
252
+ prop = pendingProps_1[_i];
253
+ if (typeof row[prop] !== 'undefined') {
254
+ payload[prop] = row[prop];
255
+ }
256
+ }
257
+ if (Object.keys(payload).length === 1) {
258
+ return [2 /*return*/];
259
+ }
260
+ return [4 /*yield*/, db.update(table, payload)];
261
+ case 2:
262
+ _a.sent();
263
+ return [2 /*return*/];
264
+ }
265
+ });
266
+ });
267
+ };
268
+ /** Raw (un-deserialized) id-ordered window — pending-ness must see stored bytes, not decrypted values. */
269
+ EncryptionLifecycleWalker.prototype.rawWindow = function (table, dbDriver, columnNames, cursor, windowSize) {
270
+ return __awaiter(this, void 0, void 0, function () {
271
+ var StatementConfigFactory, statementConfigFactory, qb;
272
+ return __generator(this, function (_a) {
273
+ switch (_a.label) {
274
+ case 0: return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require('../StatementConfigFactory')); })];
275
+ case 1:
276
+ StatementConfigFactory = (_a.sent()).StatementConfigFactory;
277
+ statementConfigFactory = new StatementConfigFactory(dbDriver.getDbName());
278
+ qb = new db_query_1.QueryBuilder(table.name).select({ fields: __spreadArray(['id'], columnNames.map(function (_a) {
279
+ var prop = _a.prop;
280
+ return prop;
281
+ }), true) });
282
+ if (cursor) {
283
+ qb.condition({ field: 'id', operator: '>', value: cursor });
284
+ }
285
+ qb.sort([{ field: 'id' }]).paginate({ start: 0, end: windowSize });
286
+ return [4 /*yield*/, dbDriver.runQuery(function (config) { return qb.toSql(statementConfigFactory.getStatementConfig(config)); })];
287
+ case 2: return [2 /*return*/, _a.sent()];
288
+ }
289
+ });
290
+ });
291
+ };
292
+ /** After decrypt-out, the decrypted columns' token rows are stale — sweep them. */
293
+ EncryptionLifecycleWalker.prototype.sweepTokenRows = function (table, db, columnNames) {
294
+ var _a;
295
+ return __awaiter(this, void 0, void 0, function () {
296
+ var tokenTable, deleteQb;
297
+ return __generator(this, function (_b) {
298
+ switch (_b.label) {
299
+ case 0:
300
+ tokenTable = (_a = this.encryptedColumns.tokenTableFor(table)) !== null && _a !== void 0 ? _a : this.encryptedColumns.tokenTableShape(table);
301
+ return [4 /*yield*/, db.tableExists(tokenTable)];
302
+ case 1:
303
+ if (!(_b.sent())) {
304
+ return [2 /*return*/];
305
+ }
306
+ deleteQb = new db_query_1.QueryBuilder(tokenTable.name).condition({
307
+ field: 'columnName',
308
+ operator: 'IN',
309
+ value: columnNames,
310
+ });
311
+ return [4 /*yield*/, db.delete(tokenTable, deleteQb)];
312
+ case 2:
313
+ _b.sent();
314
+ return [2 /*return*/];
315
+ }
316
+ });
317
+ });
318
+ };
319
+ EncryptionLifecycleWalker.prototype.resolveProps = function (table, mode, options) {
320
+ if (options.columns && options.columns.length > 0) {
321
+ for (var _i = 0, _a = options.columns; _i < _a.length; _i++) {
322
+ var prop = _a[_i];
323
+ if (!table.columns[prop]) {
324
+ throw new Error("(".concat(table.name, ") Unknown column property to walk: ").concat(prop));
325
+ }
326
+ }
327
+ return options.columns;
328
+ }
329
+ if (mode === 'decrypt') {
330
+ throw new Error("Mode 'decrypt' requires options.columns: after the config flips to encrypted: false, the " +
331
+ "declaration no longer names the columns to decrypt.");
332
+ }
333
+ var props = this.encryptedColumns.encryptedProps(table);
334
+ if (props.length === 0) {
335
+ throw new Error("(".concat(table.name, ") No encrypted columns declared \u2014 nothing to walk."));
336
+ }
337
+ return props;
338
+ };
339
+ EncryptionLifecycleWalker.DEFAULT_WINDOW_SIZE = 200;
340
+ return EncryptionLifecycleWalker;
341
+ }());
342
+ exports.EncryptionLifecycleWalker = EncryptionLifecycleWalker;
343
+ //# sourceMappingURL=EncryptionLifecycleWalker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncryptionLifecycleWalker.js","sourceRoot":"","sources":["../../../src/encryption/EncryptionLifecycleWalker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { Table } from '../Table';
2
+ import type { SerializedRecord } from '../Record';
3
+ /** Who a row being written belongs to — the data key its values encrypt under. */
4
+ export type EncryptionWriteContext = {
5
+ keyOwner: string;
6
+ };
7
+ /**
8
+ * The transparent encrypt/decrypt seam, invoked by `RecordSerializer` on every write's
9
+ * serialize and every read's deserialize — the same per-column hook layer every caller
10
+ * already passes through, so services, watchers, migrations, and queued client
11
+ * transactions get encryption without knowing it exists.
12
+ *
13
+ * - Serialize: after the columns' own `serialize` steps, values of encrypted columns are
14
+ * replaced with self-describing envelopes (`EncryptionEnvelope`), and the searchable /
15
+ * sort companions are written beside them in the same statement.
16
+ * - Deserialize: before the columns' own `deserialize` steps, envelope values are decrypted
17
+ * (the envelope names its key — owner + version — so no context is needed) and
18
+ * framework-derived companion columns are dropped. A non-envelope value in an encrypted
19
+ * column passes through unchanged — the online-adoption transition: rows written before
20
+ * the encrypt backfill stay readable while the walker converges them.
21
+ *
22
+ * Server-only: this seam executes where the database driver runs. Browser code proxies
23
+ * through `DbService` and receives decrypted values over the authenticated service.
24
+ */
25
+ export declare class EncryptionRecordHooks {
26
+ private encryptedColumns;
27
+ private envelope;
28
+ private tokenizer;
29
+ /** Encrypt encrypted-column values in `serialized` (column-name-keyed) and add companions. */
30
+ onSerialize(table: Table<any>, serialized: SerializedRecord, context?: EncryptionWriteContext): Promise<void>;
31
+ /**
32
+ * A copy of `serializedRecord` with framework companion columns dropped and every
33
+ * envelope value decrypted (any column — self-describing ciphertext also covers the
34
+ * decrypt-out transition, where the config no longer marks the column).
35
+ */
36
+ onDeserialize(table: Table<any>, serializedRecord: SerializedRecord): Promise<SerializedRecord>;
37
+ /**
38
+ * The key owner for a row being written — its permission-source scope owner:
39
+ * `DbEncryptionConfig.resolveKeyOwner` when the deployment supplies one (richer sharing
40
+ * models map scope→owner there), else the row's `scope` column value (the framework's
41
+ * scope columns hold the owning user's id).
42
+ */
43
+ resolveKeyOwnerForWrite(table: Table<any>, record: any): Promise<string>;
44
+ private internalColumnNames;
45
+ private assertServerSide;
46
+ }
47
+ //# sourceMappingURL=EncryptionRecordHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncryptionRecordHooks.d.ts","sourceRoot":"","sources":["../../../src/encryption/EncryptionRecordHooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAOlD,kFAAkF;AAClF,MAAM,MAAM,sBAAsB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,SAAS,CAAyB;IAE1C,8FAA8F;IACxF,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDnH;;;;OAIG;IACG,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0BrG;;;;;OAKG;IACG,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB9E,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,gBAAgB;CAQzB"}
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.EncryptionRecordHooks = void 0;
40
+ var DbEncryptionConfig_1 = require("./DbEncryptionConfig");
41
+ var EncryptedColumns_1 = require("./EncryptedColumns");
42
+ var EncryptionEnvelope_1 = require("./EncryptionEnvelope");
43
+ var DataKeyStore_1 = require("./DataKeyStore");
44
+ var SearchTokenizer_1 = require("./SearchTokenizer");
45
+ /**
46
+ * The transparent encrypt/decrypt seam, invoked by `RecordSerializer` on every write's
47
+ * serialize and every read's deserialize — the same per-column hook layer every caller
48
+ * already passes through, so services, watchers, migrations, and queued client
49
+ * transactions get encryption without knowing it exists.
50
+ *
51
+ * - Serialize: after the columns' own `serialize` steps, values of encrypted columns are
52
+ * replaced with self-describing envelopes (`EncryptionEnvelope`), and the searchable /
53
+ * sort companions are written beside them in the same statement.
54
+ * - Deserialize: before the columns' own `deserialize` steps, envelope values are decrypted
55
+ * (the envelope names its key — owner + version — so no context is needed) and
56
+ * framework-derived companion columns are dropped. A non-envelope value in an encrypted
57
+ * column passes through unchanged — the online-adoption transition: rows written before
58
+ * the encrypt backfill stay readable while the walker converges them.
59
+ *
60
+ * Server-only: this seam executes where the database driver runs. Browser code proxies
61
+ * through `DbService` and receives decrypted values over the authenticated service.
62
+ */
63
+ var EncryptionRecordHooks = /** @class */ (function () {
64
+ function EncryptionRecordHooks() {
65
+ this.encryptedColumns = new EncryptedColumns_1.EncryptedColumns();
66
+ this.envelope = new EncryptionEnvelope_1.EncryptionEnvelope();
67
+ this.tokenizer = new SearchTokenizer_1.SearchTokenizer();
68
+ }
69
+ /** Encrypt encrypted-column values in `serialized` (column-name-keyed) and add companions. */
70
+ EncryptionRecordHooks.prototype.onSerialize = function (table, serialized, context) {
71
+ return __awaiter(this, void 0, void 0, function () {
72
+ var touchedProps, writeKey, _i, touchedProps_1, prop, column, config, value;
73
+ return __generator(this, function (_a) {
74
+ switch (_a.label) {
75
+ case 0:
76
+ this.encryptedColumns.ensureSchema(table);
77
+ touchedProps = this.encryptedColumns
78
+ .encryptedProps(table)
79
+ .filter(function (prop) { return table.columns[prop].name in serialized; });
80
+ if (touchedProps.length === 0) {
81
+ return [2 /*return*/];
82
+ }
83
+ this.assertServerSide();
84
+ _i = 0, touchedProps_1 = touchedProps;
85
+ _a.label = 1;
86
+ case 1:
87
+ if (!(_i < touchedProps_1.length)) return [3 /*break*/, 5];
88
+ prop = touchedProps_1[_i];
89
+ column = table.columns[prop];
90
+ config = this.encryptedColumns.configFor(table, prop);
91
+ value = serialized[column.name];
92
+ if (value === null) {
93
+ // Null stays null (IS NULL queries keep working); companions null out with it.
94
+ if (config.searchable === 'equality') {
95
+ serialized["".concat(column.name, "_enc_eq")] = null;
96
+ }
97
+ if (config.sortKey) {
98
+ serialized["".concat(column.name, "_enc_srt")] = null;
99
+ }
100
+ return [3 /*break*/, 4];
101
+ }
102
+ if (typeof value !== 'string') {
103
+ throw new DbEncryptionConfig_1.EncryptedColumnConfigError("(".concat(table.name, ".").concat(prop, ") encrypted columns must serialize to a string; ") +
104
+ "got ".concat(typeof value, ". Declare 'encrypted' on a StringColumn-family column."));
105
+ }
106
+ if (!!writeKey) return [3 /*break*/, 3];
107
+ if (!(context === null || context === void 0 ? void 0 : context.keyOwner)) {
108
+ throw new DbEncryptionConfig_1.EncryptedColumnConfigError("(".concat(table.name, ") A write touches encrypted column '").concat(prop, "' but no key owner was ") +
109
+ "resolved. Writes to encrypted columns must resolve the row's scope owner " +
110
+ "(the row's 'scope' value, or DbEncryptionConfig.resolveKeyOwner).");
111
+ }
112
+ return [4 /*yield*/, new DataKeyStore_1.DataKeyStore().getWriteKey(context.keyOwner)];
113
+ case 2:
114
+ writeKey = _a.sent();
115
+ _a.label = 3;
116
+ case 3:
117
+ serialized[column.name] = this.envelope.encrypt(value, writeKey);
118
+ if (config.searchable === 'equality') {
119
+ serialized["".concat(column.name, "_enc_eq")] = this.tokenizer.equalityFingerprint(value, writeKey.indexKey);
120
+ }
121
+ if (config.sortKey) {
122
+ serialized["".concat(column.name, "_enc_srt")] = this.tokenizer.sortPrefix(value, config.sortKey.revealPrefix);
123
+ }
124
+ _a.label = 4;
125
+ case 4:
126
+ _i++;
127
+ return [3 /*break*/, 1];
128
+ case 5: return [2 /*return*/];
129
+ }
130
+ });
131
+ });
132
+ };
133
+ /**
134
+ * A copy of `serializedRecord` with framework companion columns dropped and every
135
+ * envelope value decrypted (any column — self-describing ciphertext also covers the
136
+ * decrypt-out transition, where the config no longer marks the column).
137
+ */
138
+ EncryptionRecordHooks.prototype.onDeserialize = function (table, serializedRecord) {
139
+ return __awaiter(this, void 0, void 0, function () {
140
+ var prepared, internalColumnNames, decrypted, _i, _a, columnName, value, parsed, key;
141
+ return __generator(this, function (_b) {
142
+ switch (_b.label) {
143
+ case 0:
144
+ this.encryptedColumns.ensureSchema(table);
145
+ prepared = {};
146
+ internalColumnNames = this.internalColumnNames(table);
147
+ decrypted = false;
148
+ _i = 0, _a = Object.keys(serializedRecord);
149
+ _b.label = 1;
150
+ case 1:
151
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
152
+ columnName = _a[_i];
153
+ if (internalColumnNames.has(columnName)) {
154
+ return [3 /*break*/, 3];
155
+ }
156
+ value = serializedRecord[columnName];
157
+ parsed = this.envelope.parse(value);
158
+ if (!parsed) {
159
+ prepared[columnName] = value;
160
+ return [3 /*break*/, 3];
161
+ }
162
+ this.assertServerSide();
163
+ return [4 /*yield*/, new DataKeyStore_1.DataKeyStore().getKeyByVersion(parsed.owner, parsed.version)];
164
+ case 2:
165
+ key = _b.sent();
166
+ prepared[columnName] = this.envelope.decrypt(value, key);
167
+ decrypted = true;
168
+ _b.label = 3;
169
+ case 3:
170
+ _i++;
171
+ return [3 /*break*/, 1];
172
+ case 4: return [2 /*return*/, decrypted || internalColumnNames.size > 0 ? prepared : serializedRecord];
173
+ }
174
+ });
175
+ });
176
+ };
177
+ /**
178
+ * The key owner for a row being written — its permission-source scope owner:
179
+ * `DbEncryptionConfig.resolveKeyOwner` when the deployment supplies one (richer sharing
180
+ * models map scope→owner there), else the row's `scope` column value (the framework's
181
+ * scope columns hold the owning user's id).
182
+ */
183
+ EncryptionRecordHooks.prototype.resolveKeyOwnerForWrite = function (table, record) {
184
+ return __awaiter(this, void 0, void 0, function () {
185
+ var config, owner;
186
+ return __generator(this, function (_a) {
187
+ switch (_a.label) {
188
+ case 0:
189
+ config = (0, DbEncryptionConfig_1.getDbEncryptionConfig)();
190
+ if (!config.resolveKeyOwner) return [3 /*break*/, 2];
191
+ return [4 /*yield*/, config.resolveKeyOwner({ table: table, record: record })];
192
+ case 1:
193
+ owner = _a.sent();
194
+ if (owner) {
195
+ return [2 /*return*/, owner];
196
+ }
197
+ _a.label = 2;
198
+ case 2:
199
+ if (typeof record.scope === 'string' && record.scope.length > 0) {
200
+ return [2 /*return*/, record.scope];
201
+ }
202
+ throw new DbEncryptionConfig_1.EncryptedColumnConfigError("(".concat(table.name, ") Cannot resolve the key owner for a write to an encrypted column: the row ") +
203
+ "carries no 'scope' value and DbEncryptionConfig.resolveKeyOwner resolved nothing. " +
204
+ "Scoped tables get this automatically; other tables must supply resolveKeyOwner.");
205
+ }
206
+ });
207
+ });
208
+ };
209
+ EncryptionRecordHooks.prototype.internalColumnNames = function (table) {
210
+ var names = new Set();
211
+ for (var _i = 0, _a = Object.keys(table.columns); _i < _a.length; _i++) {
212
+ var prop = _a[_i];
213
+ var column = table.columns[prop];
214
+ if (this.encryptedColumns.isInternalColumn(column)) {
215
+ names.add(column.name);
216
+ }
217
+ }
218
+ return names;
219
+ };
220
+ EncryptionRecordHooks.prototype.assertServerSide = function () {
221
+ if (typeof self !== 'undefined') {
222
+ throw new Error("Column encryption executes where the database driver runs (the server); the browser " +
223
+ "must reach encrypted tables through DbService.");
224
+ }
225
+ };
226
+ return EncryptionRecordHooks;
227
+ }());
228
+ exports.EncryptionRecordHooks = EncryptionRecordHooks;
229
+ //# sourceMappingURL=EncryptionRecordHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncryptionRecordHooks.js","sourceRoot":"","sources":["../../../src/encryption/EncryptionRecordHooks.ts"],"names":[],"mappings":"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"}