@proteinjs/user-server 1.7.0 → 1.8.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 (102) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/generated/index.d.ts.map +1 -1
  3. package/dist/generated/index.js +9 -1
  4. package/dist/generated/index.js.map +1 -1
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +6 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/authentication/PasswordHasher.d.ts +45 -0
  10. package/dist/src/authentication/PasswordHasher.d.ts.map +1 -0
  11. package/dist/src/authentication/PasswordHasher.js +152 -0
  12. package/dist/src/authentication/PasswordHasher.js.map +1 -0
  13. package/dist/src/authentication/UserStatusTableWatcher.d.ts +19 -0
  14. package/dist/src/authentication/UserStatusTableWatcher.d.ts.map +1 -0
  15. package/dist/src/authentication/UserStatusTableWatcher.js +115 -0
  16. package/dist/src/authentication/UserStatusTableWatcher.js.map +1 -0
  17. package/dist/src/authentication/authenticate.d.ts.map +1 -1
  18. package/dist/src/authentication/authenticate.js +41 -13
  19. package/dist/src/authentication/authenticate.js.map +1 -1
  20. package/dist/src/authorization/userCache.d.ts.map +1 -1
  21. package/dist/src/authorization/userCache.js +10 -1
  22. package/dist/src/authorization/userCache.js.map +1 -1
  23. package/dist/src/emails/AccountDeletionEmailConfigs.d.ts +35 -0
  24. package/dist/src/emails/AccountDeletionEmailConfigs.d.ts.map +1 -0
  25. package/dist/src/emails/AccountDeletionEmailConfigs.js +36 -0
  26. package/dist/src/emails/AccountDeletionEmailConfigs.js.map +1 -0
  27. package/dist/src/emails/AccountDeletionEmails.d.ts +13 -0
  28. package/dist/src/emails/AccountDeletionEmails.d.ts.map +1 -0
  29. package/dist/src/emails/AccountDeletionEmails.js +98 -0
  30. package/dist/src/emails/AccountDeletionEmails.js.map +1 -0
  31. package/dist/src/routes/executePasswordReset.js +5 -3
  32. package/dist/src/routes/executePasswordReset.js.map +1 -1
  33. package/dist/src/routes/login.d.ts.map +1 -1
  34. package/dist/src/routes/login.js +26 -2
  35. package/dist/src/routes/login.js.map +1 -1
  36. package/dist/src/services/AccountDeletion.d.ts +52 -0
  37. package/dist/src/services/AccountDeletion.d.ts.map +1 -0
  38. package/dist/src/services/AccountDeletion.js +426 -0
  39. package/dist/src/services/AccountDeletion.js.map +1 -0
  40. package/dist/src/services/MachineCredentials.d.ts +21 -0
  41. package/dist/src/services/MachineCredentials.d.ts.map +1 -0
  42. package/dist/src/services/MachineCredentials.js +164 -0
  43. package/dist/src/services/MachineCredentials.js.map +1 -0
  44. package/dist/src/services/Roles.d.ts.map +1 -1
  45. package/dist/src/services/Roles.js +7 -0
  46. package/dist/src/services/Roles.js.map +1 -1
  47. package/dist/src/services/SetUserStatus.d.ts +18 -0
  48. package/dist/src/services/SetUserStatus.d.ts.map +1 -0
  49. package/dist/src/services/SetUserStatus.js +118 -0
  50. package/dist/src/services/SetUserStatus.js.map +1 -0
  51. package/dist/src/services/Signup.d.ts +2 -2
  52. package/dist/src/services/Signup.d.ts.map +1 -1
  53. package/dist/src/services/Signup.js +22 -17
  54. package/dist/src/services/Signup.js.map +1 -1
  55. package/dist/src/services/UpdateUserInfo.d.ts.map +1 -1
  56. package/dist/src/services/UpdateUserInfo.js +20 -17
  57. package/dist/src/services/UpdateUserInfo.js.map +1 -1
  58. package/dist/test/AccountDeletion.integration.test.d.ts +2 -0
  59. package/dist/test/AccountDeletion.integration.test.d.ts.map +1 -0
  60. package/dist/test/AccountDeletion.integration.test.js +710 -0
  61. package/dist/test/AccountDeletion.integration.test.js.map +1 -0
  62. package/dist/test/DevLogin.test.js +12 -11
  63. package/dist/test/DevLogin.test.js.map +1 -1
  64. package/dist/test/MachineAccounts.integration.test.d.ts +2 -0
  65. package/dist/test/MachineAccounts.integration.test.d.ts.map +1 -0
  66. package/dist/test/MachineAccounts.integration.test.js +597 -0
  67. package/dist/test/MachineAccounts.integration.test.js.map +1 -0
  68. package/dist/test/PasswordMigration.integration.test.d.ts +2 -0
  69. package/dist/test/PasswordMigration.integration.test.d.ts.map +1 -0
  70. package/dist/test/PasswordMigration.integration.test.js +332 -0
  71. package/dist/test/PasswordMigration.integration.test.js.map +1 -0
  72. package/dist/test/SetUserStatus.integration.test.d.ts +2 -0
  73. package/dist/test/SetUserStatus.integration.test.d.ts.map +1 -0
  74. package/dist/test/SetUserStatus.integration.test.js +266 -0
  75. package/dist/test/SetUserStatus.integration.test.js.map +1 -0
  76. package/dist/test/UserStatusGate.integration.test.d.ts +2 -0
  77. package/dist/test/UserStatusGate.integration.test.d.ts.map +1 -0
  78. package/dist/test/UserStatusGate.integration.test.js +162 -0
  79. package/dist/test/UserStatusGate.integration.test.js.map +1 -0
  80. package/generated/index.ts +9 -1
  81. package/index.ts +3 -0
  82. package/package.json +8 -6
  83. package/src/authentication/PasswordHasher.ts +87 -0
  84. package/src/authentication/UserStatusTableWatcher.ts +56 -0
  85. package/src/authentication/authenticate.ts +31 -6
  86. package/src/authorization/userCache.ts +9 -1
  87. package/src/emails/AccountDeletionEmailConfigs.ts +79 -0
  88. package/src/emails/AccountDeletionEmails.ts +39 -0
  89. package/src/routes/executePasswordReset.ts +2 -2
  90. package/src/routes/login.ts +23 -0
  91. package/src/services/AccountDeletion.ts +291 -0
  92. package/src/services/MachineCredentials.ts +94 -0
  93. package/src/services/Roles.ts +10 -0
  94. package/src/services/SetUserStatus.ts +56 -0
  95. package/src/services/Signup.ts +4 -4
  96. package/src/services/UpdateUserInfo.ts +4 -5
  97. package/test/AccountDeletion.integration.test.ts +391 -0
  98. package/test/DevLogin.test.ts +5 -4
  99. package/test/MachineAccounts.integration.test.ts +312 -0
  100. package/test/PasswordMigration.integration.test.ts +132 -0
  101. package/test/SetUserStatus.integration.test.ts +120 -0
  102. package/test/UserStatusGate.integration.test.ts +68 -0
@@ -0,0 +1,426 @@
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
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
39
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
40
+ if (ar || !(i in from)) {
41
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
42
+ ar[i] = from[i];
43
+ }
44
+ }
45
+ return to.concat(ar || Array.prototype.slice.call(from));
46
+ };
47
+ var __importDefault = (this && this.__importDefault) || function (mod) {
48
+ return (mod && mod.__esModule) ? mod : { "default": mod };
49
+ };
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.AccountDeletion = void 0;
52
+ var moment_1 = __importDefault(require("moment"));
53
+ var db_1 = require("@proteinjs/db");
54
+ var logger_1 = require("@proteinjs/logger");
55
+ var user_1 = require("@proteinjs/user");
56
+ var DefaultAdminCredentials_1 = require("../authentication/DefaultAdminCredentials");
57
+ var PasswordHasher_1 = require("../authentication/PasswordHasher");
58
+ var AccountDeletionEmails_1 = require("../emails/AccountDeletionEmails");
59
+ var SetUserStatus_1 = require("./SetUserStatus");
60
+ /** IN-list chunk size for grant enumeration/revocation (the Db.ts IN-chunk precedent). */
61
+ var GRANT_CHUNK_SIZE = 100;
62
+ /**
63
+ * Deactivation ("gone right away") + cancel-by-login — the synchronous half of archive-then-purge.
64
+ * The purge walker (flow-server) owns erasure after the grace window; this service owns entering
65
+ * and leaving the deactivated state, driven by the `account_deletion` manifest row.
66
+ *
67
+ * Deliberately NOT deactivation's job: the user's own content_reference/content_seen/pins/
68
+ * notifications — all scoped to the locked-out user, invisible behind the auth gate, purged by
69
+ * the walker. Leaving them preserves watermarks and pins exactly for cancel-restore.
70
+ */
71
+ var AccountDeletion = /** @class */ (function () {
72
+ function AccountDeletion() {
73
+ /** Any authenticated user — the call acts only on the session user (re-auth inside). */
74
+ this.serviceMetadata = {
75
+ auth: {
76
+ allUsers: true,
77
+ },
78
+ };
79
+ this.logger = new logger_1.Logger({ name: this.constructor.name });
80
+ }
81
+ /**
82
+ * §3.3 — seven steps, each idempotent; a re-call after a partial failure resumes from the
83
+ * stored manifest. The caller's sessions die last, so the response is the last authenticated
84
+ * exchange.
85
+ */
86
+ AccountDeletion.prototype.requestDeletion = function (password) {
87
+ return __awaiter(this, void 0, void 0, function () {
88
+ var user, db, deletion, error_1;
89
+ return __generator(this, function (_a) {
90
+ switch (_a.label) {
91
+ case 0: return [4 /*yield*/, this.reauthenticateSessionUser(password)];
92
+ case 1:
93
+ user = _a.sent();
94
+ db = (0, db_1.getDbAsSystem)();
95
+ return [4 /*yield*/, db.get(user_1.tables.AccountDeletion, { userId: user.id })];
96
+ case 2:
97
+ deletion = _a.sent();
98
+ if (!!deletion) return [3 /*break*/, 4];
99
+ return [4 /*yield*/, this.createDeletionManifest(db, user)];
100
+ case 3:
101
+ deletion = _a.sent();
102
+ _a.label = 4;
103
+ case 4:
104
+ // 4. Revoke. Watchers fire per Db.delete: the grant watcher removes grantee-side cards for
105
+ // my content AND my cards for others' content — the synchronous "gone right away" contract.
106
+ // Already-deleted ids no-op (resume-safe).
107
+ return [4 /*yield*/, this.deleteGrantsById(db, deletion.manifestGrants.map(function (grant) { return grant.id; }))];
108
+ case 5:
109
+ // 4. Revoke. Watchers fire per Db.delete: the grant watcher removes grantee-side cards for
110
+ // my content AND my cards for others' content — the synchronous "gone right away" contract.
111
+ // Already-deleted ids no-op (resume-safe).
112
+ _a.sent();
113
+ // 5. Flip the user row through the ONE standing write path (audited), then stamp the
114
+ // deletion columns. deleteRequestedAt = the manifest row's own creation time, so a resumed
115
+ // call re-writes identical values.
116
+ return [4 /*yield*/, new SetUserStatus_1.SetUserStatus().setUserStatus(user.id, 'deactivated')];
117
+ case 6:
118
+ // 5. Flip the user row through the ONE standing write path (audited), then stamp the
119
+ // deletion columns. deleteRequestedAt = the manifest row's own creation time, so a resumed
120
+ // call re-writes identical values.
121
+ _a.sent();
122
+ return [4 /*yield*/, db.update(user_1.tables.User, {
123
+ id: user.id,
124
+ deleteRequestedAt: deletion.created,
125
+ purgeAfter: deletion.purgeAfter,
126
+ })];
127
+ case 7:
128
+ _a.sent();
129
+ _a.label = 8;
130
+ case 8:
131
+ _a.trys.push([8, 10, , 11]);
132
+ return [4 /*yield*/, new AccountDeletionEmails_1.AccountDeletionEmails().sendDeletionRequested(user.email, deletion.purgeAfter)];
133
+ case 9:
134
+ _a.sent();
135
+ return [3 /*break*/, 11];
136
+ case 10:
137
+ error_1 = _a.sent();
138
+ this.logger.error({
139
+ message: 'Failed to send the deletion-requested email',
140
+ obj: { userId: user.id },
141
+ error: error_1,
142
+ });
143
+ return [3 /*break*/, 11];
144
+ case 11:
145
+ // 6. Kill sessions last — SocketIOSessionWatcher disconnects every live socket, including
146
+ // the caller's own.
147
+ return [4 /*yield*/, db.delete(user_1.tables.Session, { userEmail: user.email })];
148
+ case 12:
149
+ // 6. Kill sessions last — SocketIOSessionWatcher disconnects every live socket, including
150
+ // the caller's own.
151
+ _a.sent();
152
+ // 7. The UI's confirmation copy renders from this.
153
+ return [2 /*return*/, { purgeAfter: deletion.purgeAfter }];
154
+ }
155
+ });
156
+ });
157
+ };
158
+ /**
159
+ * §3.4 — logging back in IS the cancel (called by the login route after `authenticate`
160
+ * passes, BEFORE `request.login`). Not RPC-reachable: it is deliberately absent from the
161
+ * AccountDeletionService interface, so the service router never exposes it.
162
+ */
163
+ AccountDeletion.prototype.cancelPendingDeletion = function (email) {
164
+ var _a, _b;
165
+ return __awaiter(this, void 0, void 0, function () {
166
+ var db, deletion, claimSeq, claimQb, claimed, current;
167
+ return __generator(this, function (_c) {
168
+ switch (_c.label) {
169
+ case 0:
170
+ db = (0, db_1.getDbAsSystem)();
171
+ return [4 /*yield*/, db.get(user_1.tables.AccountDeletion, { userEmail: email.toLowerCase() })];
172
+ case 1:
173
+ deletion = _c.sent();
174
+ if (!deletion) {
175
+ return [2 /*return*/, 'not-pending'];
176
+ }
177
+ claimSeq = ((_a = deletion.leaseSeq) !== null && _a !== void 0 ? _a : 0) + 1;
178
+ claimQb = new db_1.QueryBuilderFactory()
179
+ .createQueryBuilder(user_1.tables.AccountDeletion)
180
+ .condition({ field: 'userId', operator: '=', value: deletion.userId })
181
+ .condition({ field: 'phase', operator: 'IN', value: ['grace', 'restoring'] })
182
+ .condition({ field: 'leaseSeq', operator: '=', value: (_b = deletion.leaseSeq) !== null && _b !== void 0 ? _b : 0 });
183
+ return [4 /*yield*/, db.update(user_1.tables.AccountDeletion, { phase: 'restoring', leaseSeq: claimSeq }, claimQb)];
184
+ case 2:
185
+ claimed = _c.sent();
186
+ if (!(claimed !== 1)) return [3 /*break*/, 4];
187
+ return [4 /*yield*/, db.get(user_1.tables.AccountDeletion, { userId: deletion.userId })];
188
+ case 3:
189
+ current = _c.sent();
190
+ if (!current) {
191
+ return [2 /*return*/, 'not-pending']; // a concurrent cancel finished the restore — normal login proceeds
192
+ }
193
+ if (current.phase === 'purging' || current.phase === 'purged') {
194
+ return [2 /*return*/, 'purging']; // the walker won — no longer restorable
195
+ }
196
+ throw new Error("A concurrent restore is in flight for account ".concat(deletion.userId, " \u2014 retry login."));
197
+ case 4:
198
+ // Re-insert manifest grants idempotently (fresh ids). The grant watcher's afterInsert
199
+ // rebuilds content_reference cards through the normal path — grantees' cards for my content
200
+ // AND my cards for inbound shares — synchronously, before the login response.
201
+ return [4 /*yield*/, this.restoreManifestGrants(db, deletion.manifestGrants)];
202
+ case 5:
203
+ // Re-insert manifest grants idempotently (fresh ids). The grant watcher's afterInsert
204
+ // rebuilds content_reference cards through the normal path — grantees' cards for my content
205
+ // AND my cards for inbound shares — synchronously, before the login response.
206
+ _c.sent();
207
+ // User row back to standing through the audited path; deletion stamps nulled.
208
+ return [4 /*yield*/, new SetUserStatus_1.SetUserStatus().setUserStatus(deletion.userId, 'active')];
209
+ case 6:
210
+ // User row back to standing through the audited path; deletion stamps nulled.
211
+ _c.sent();
212
+ return [4 /*yield*/, db.update(user_1.tables.User, { id: deletion.userId, deleteRequestedAt: null, purgeAfter: null })];
213
+ case 7:
214
+ _c.sent();
215
+ return [4 /*yield*/, db.delete(user_1.tables.AccountDeletion, { id: deletion.id })];
216
+ case 8:
217
+ _c.sent();
218
+ return [2 /*return*/, 'restored'];
219
+ }
220
+ });
221
+ });
222
+ };
223
+ /** Verify the caller's password against their own account row; throws with nothing written. */
224
+ AccountDeletion.prototype.reauthenticateSessionUser = function (password) {
225
+ return __awaiter(this, void 0, void 0, function () {
226
+ var sessionUser, adminCredentials, user, _a;
227
+ return __generator(this, function (_b) {
228
+ switch (_b.label) {
229
+ case 0:
230
+ sessionUser = new user_1.UserRepo().getUser();
231
+ adminCredentials = DefaultAdminCredentials_1.DefaultAdminCredentials.getCredentials();
232
+ if (adminCredentials && sessionUser.email === adminCredentials.username) {
233
+ throw new Error('The default admin session has no account row to delete.');
234
+ }
235
+ if (!sessionUser.id || !sessionUser.email) {
236
+ throw new Error('Account deletion requires a signed-in account.');
237
+ }
238
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(user_1.tables.User, { email: sessionUser.email.toLowerCase() })];
239
+ case 1:
240
+ user = _b.sent();
241
+ _a = !user;
242
+ if (_a) return [3 /*break*/, 3];
243
+ return [4 /*yield*/, new PasswordHasher_1.PasswordHasher().verify(user.password, password)];
244
+ case 2:
245
+ _a = !(_b.sent());
246
+ _b.label = 3;
247
+ case 3:
248
+ if (_a) {
249
+ throw new Error('Password incorrect');
250
+ }
251
+ return [2 /*return*/, user];
252
+ }
253
+ });
254
+ });
255
+ };
256
+ /**
257
+ * §3.3 step 3: enumerate grants in both directions and persist the manifest row — phase
258
+ * 'grace', full grant set, owned-resource snapshot — before any mutation.
259
+ */
260
+ AccountDeletion.prototype.createDeletionManifest = function (db, user) {
261
+ return __awaiter(this, void 0, void 0, function () {
262
+ var qbf, ownerQb, ownerGrants, ownedResourceIds, inboundQb, inbound, outbound, _i, _a, idsChunk, outboundQb, grants, graceDays;
263
+ var _this = this;
264
+ return __generator(this, function (_b) {
265
+ switch (_b.label) {
266
+ case 0:
267
+ qbf = new db_1.QueryBuilderFactory();
268
+ ownerQb = qbf
269
+ .createQueryBuilder(user_1.tables.AccessGrant)
270
+ .condition({ field: 'principal', operator: '=', value: user.id })
271
+ .condition({ field: 'accessLevel', operator: '=', value: 'owner' });
272
+ return [4 /*yield*/, db.query(user_1.tables.AccessGrant, ownerQb)];
273
+ case 1:
274
+ ownerGrants = _b.sent();
275
+ ownedResourceIds = Array.from(new Set(ownerGrants.map(function (grant) { return _this.referenceId(grant.resource, grant.id); })));
276
+ inboundQb = qbf
277
+ .createQueryBuilder(user_1.tables.AccessGrant)
278
+ .condition({ field: 'principal', operator: '=', value: user.id })
279
+ .condition({ field: 'accessLevel', operator: 'IN', value: ['read', 'write', 'admin'] });
280
+ return [4 /*yield*/, db.query(user_1.tables.AccessGrant, inboundQb)];
281
+ case 2:
282
+ inbound = _b.sent();
283
+ outbound = [];
284
+ _i = 0, _a = this.chunk(ownedResourceIds, GRANT_CHUNK_SIZE);
285
+ _b.label = 3;
286
+ case 3:
287
+ if (!(_i < _a.length)) return [3 /*break*/, 6];
288
+ idsChunk = _a[_i];
289
+ outboundQb = qbf
290
+ .createQueryBuilder(user_1.tables.AccessGrant)
291
+ .condition({ field: 'resource', operator: 'IN', value: idsChunk });
292
+ return [4 /*yield*/, db.query(user_1.tables.AccessGrant, outboundQb)];
293
+ case 4:
294
+ grants = _b.sent();
295
+ outbound.push.apply(outbound, grants.filter(function (grant) { return _this.referenceId(grant.principal, grant.id) !== user.id; }));
296
+ _b.label = 5;
297
+ case 5:
298
+ _i++;
299
+ return [3 /*break*/, 3];
300
+ case 6:
301
+ graceDays = this.gracePeriodDays();
302
+ return [4 /*yield*/, db.insert(user_1.tables.AccountDeletion, {
303
+ userId: user.id,
304
+ userEmail: user.email,
305
+ phase: 'grace',
306
+ purgeAfter: (0, moment_1.default)().add(Math.round(graceDays * 24 * 60 * 60 * 1000), 'milliseconds'),
307
+ manifestGrants: __spreadArray(__spreadArray([], inbound, true), outbound, true).map(function (grant) { return _this.toManifestGrant(grant); }),
308
+ ownedResourceIds: ownedResourceIds,
309
+ leaseSeq: 0,
310
+ })];
311
+ case 7: return [2 /*return*/, _b.sent()];
312
+ }
313
+ });
314
+ });
315
+ };
316
+ /** Revoke grants by manifest id, in chunks of 100. Already-deleted ids no-op. */
317
+ AccountDeletion.prototype.deleteGrantsById = function (db, grantIds) {
318
+ return __awaiter(this, void 0, void 0, function () {
319
+ var _i, _a, idsChunk, qb;
320
+ return __generator(this, function (_b) {
321
+ switch (_b.label) {
322
+ case 0:
323
+ _i = 0, _a = this.chunk(grantIds, GRANT_CHUNK_SIZE);
324
+ _b.label = 1;
325
+ case 1:
326
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
327
+ idsChunk = _a[_i];
328
+ qb = new db_1.QueryBuilderFactory()
329
+ .createQueryBuilder(user_1.tables.AccessGrant)
330
+ .condition({ field: 'id', operator: 'IN', value: idsChunk });
331
+ return [4 /*yield*/, db.delete(user_1.tables.AccessGrant, qb)];
332
+ case 2:
333
+ _b.sent();
334
+ _b.label = 3;
335
+ case 3:
336
+ _i++;
337
+ return [3 /*break*/, 1];
338
+ case 4: return [2 /*return*/];
339
+ }
340
+ });
341
+ });
342
+ };
343
+ /** §3.4 step 3: per entry, get by (principal, resource, accessLevel) → insert if missing. */
344
+ AccountDeletion.prototype.restoreManifestGrants = function (db, manifestGrants) {
345
+ var _a;
346
+ return __awaiter(this, void 0, void 0, function () {
347
+ var _i, manifestGrants_1, grant, existingQb, existing;
348
+ return __generator(this, function (_b) {
349
+ switch (_b.label) {
350
+ case 0:
351
+ _i = 0, manifestGrants_1 = manifestGrants;
352
+ _b.label = 1;
353
+ case 1:
354
+ if (!(_i < manifestGrants_1.length)) return [3 /*break*/, 5];
355
+ grant = manifestGrants_1[_i];
356
+ existingQb = new db_1.QueryBuilderFactory()
357
+ .createQueryBuilder(user_1.tables.AccessGrant)
358
+ .condition({ field: 'principal', operator: '=', value: grant.principal })
359
+ .condition({ field: 'resource', operator: '=', value: grant.resource })
360
+ .condition({ field: 'accessLevel', operator: '=', value: grant.accessLevel });
361
+ return [4 /*yield*/, db.query(user_1.tables.AccessGrant, existingQb)];
362
+ case 2:
363
+ existing = _b.sent();
364
+ if (existing.length > 0) {
365
+ return [3 /*break*/, 4];
366
+ }
367
+ return [4 /*yield*/, db.insert(user_1.tables.AccessGrant, {
368
+ principal: new db_1.Reference(user_1.tables.User.name, grant.principal),
369
+ // Serialization fails loudly on a missing table name — a table-less grant is corrupt.
370
+ resource: new db_1.Reference((_a = grant.resourceTable) !== null && _a !== void 0 ? _a : '', grant.resource),
371
+ resourceTable: grant.resourceTable,
372
+ accessLevel: grant.accessLevel,
373
+ })];
374
+ case 3:
375
+ _b.sent();
376
+ _b.label = 4;
377
+ case 4:
378
+ _i++;
379
+ return [3 /*break*/, 1];
380
+ case 5: return [2 /*return*/];
381
+ }
382
+ });
383
+ });
384
+ };
385
+ AccountDeletion.prototype.toManifestGrant = function (grant) {
386
+ return {
387
+ id: grant.id,
388
+ principal: this.referenceId(grant.principal, grant.id),
389
+ resource: this.referenceId(grant.resource, grant.id),
390
+ resourceTable: grant.resourceTable,
391
+ accessLevel: grant.accessLevel,
392
+ };
393
+ };
394
+ /** A grant reference's id, loudly — the manifest must never be built from corrupt grants. */
395
+ AccountDeletion.prototype.referenceId = function (reference, grantId) {
396
+ if (!(reference === null || reference === void 0 ? void 0 : reference._id)) {
397
+ throw new Error("access_grant ".concat(grantId, " has a reference with no id \u2014 refusing to build a deletion manifest."));
398
+ }
399
+ return reference._id;
400
+ };
401
+ /**
402
+ * Grace window in days (ACCOUNT_DELETION_GRACE_DAYS, default 30). Fractional values are
403
+ * honored so dev verification can shorten the window to minutes.
404
+ */
405
+ AccountDeletion.prototype.gracePeriodDays = function () {
406
+ var raw = process.env.ACCOUNT_DELETION_GRACE_DAYS;
407
+ if (raw === undefined || raw === '') {
408
+ return 30;
409
+ }
410
+ var days = Number(raw);
411
+ if (!Number.isFinite(days) || days < 0) {
412
+ throw new Error("ACCOUNT_DELETION_GRACE_DAYS must be a non-negative number of days, got '".concat(raw, "'"));
413
+ }
414
+ return days;
415
+ };
416
+ AccountDeletion.prototype.chunk = function (items, size) {
417
+ var chunks = [];
418
+ for (var i = 0; i < items.length; i += size) {
419
+ chunks.push(items.slice(i, i + size));
420
+ }
421
+ return chunks;
422
+ };
423
+ return AccountDeletion;
424
+ }());
425
+ exports.AccountDeletion = AccountDeletion;
426
+ //# sourceMappingURL=AccountDeletion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountDeletion.js","sourceRoot":"","sources":["../../../src/services/AccountDeletion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { MachineAccountView, MachineCredentialsService, MintedMachineCredential } from '@proteinjs/user';
2
+ import { Service } from '@proteinjs/service';
3
+ /**
4
+ * Credential minting for code-declared machine accounts — the ONLY manual step in a machine
5
+ * account's life (identity and grants come from the `MachineAccount` declaration at boot).
6
+ *
7
+ * Generates a strong random password (never human-chosen), stores its hash on the account row
8
+ * via PasswordHasher's machine mode — sha256, deliberately not the argon2id human KDF: a
9
+ * generated 256-bit secret gains nothing from key stretching, and the bridge logs in fresh per
10
+ * poll, so verifies stay cheap. (The plaintext is never persisted or logged.) Kills the
11
+ * account's existing sessions (the old
12
+ * credential dies with them), and returns the plaintext ONCE for pasting into the declaration's
13
+ * Secret Manager secret. The same call rotates. Machine rows only (`is_loaded_from_source`);
14
+ * human credentials go through the password-reset flow.
15
+ */
16
+ export declare class MachineCredentials implements MachineCredentialsService {
17
+ serviceMetadata: Service['serviceMetadata'];
18
+ listMachineAccounts(): Promise<MachineAccountView[]>;
19
+ mintCredential(email: string): Promise<MintedMachineCredential>;
20
+ }
21
+ //# sourceMappingURL=MachineCredentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MachineCredentials.d.ts","sourceRoot":"","sources":["../../../src/services/MachineCredentials.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EAIxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,yBAAyB;IAC3D,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAIhD;IAEI,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAmBpD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAwCtE"}
@@ -0,0 +1,164 @@
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
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
39
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
40
+ if (ar || !(i in from)) {
41
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
42
+ ar[i] = from[i];
43
+ }
44
+ }
45
+ return to.concat(ar || Array.prototype.slice.call(from));
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.MachineCredentials = void 0;
49
+ var crypto_1 = require("crypto");
50
+ var db_1 = require("@proteinjs/db");
51
+ var user_1 = require("@proteinjs/user");
52
+ var logger_1 = require("@proteinjs/logger");
53
+ var PasswordHasher_1 = require("../authentication/PasswordHasher");
54
+ /**
55
+ * Credential minting for code-declared machine accounts — the ONLY manual step in a machine
56
+ * account's life (identity and grants come from the `MachineAccount` declaration at boot).
57
+ *
58
+ * Generates a strong random password (never human-chosen), stores its hash on the account row
59
+ * via PasswordHasher's machine mode — sha256, deliberately not the argon2id human KDF: a
60
+ * generated 256-bit secret gains nothing from key stretching, and the bridge logs in fresh per
61
+ * poll, so verifies stay cheap. (The plaintext is never persisted or logged.) Kills the
62
+ * account's existing sessions (the old
63
+ * credential dies with them), and returns the plaintext ONCE for pasting into the declaration's
64
+ * Secret Manager secret. The same call rotates. Machine rows only (`is_loaded_from_source`);
65
+ * human credentials go through the password-reset flow.
66
+ */
67
+ var MachineCredentials = /** @class */ (function () {
68
+ function MachineCredentials() {
69
+ this.serviceMetadata = {
70
+ auth: {
71
+ permission: user_1.USER_PERMISSIONS.users,
72
+ },
73
+ };
74
+ }
75
+ MachineCredentials.prototype.listMachineAccounts = function () {
76
+ return __awaiter(this, void 0, void 0, function () {
77
+ var db, views, _i, _a, declaration, row, booted;
78
+ return __generator(this, function (_b) {
79
+ switch (_b.label) {
80
+ case 0:
81
+ db = (0, db_1.getDbAsSystem)();
82
+ views = [];
83
+ _i = 0, _a = (0, user_1.getMachineAccounts)();
84
+ _b.label = 1;
85
+ case 1:
86
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
87
+ declaration = _a[_i];
88
+ return [4 /*yield*/, db.get(user_1.tables.User, { email: declaration.email })];
89
+ case 2:
90
+ row = _b.sent();
91
+ booted = !!row && row.isLoadedFromSource === true;
92
+ views.push({
93
+ email: declaration.email,
94
+ accountName: declaration.accountName,
95
+ roles: __spreadArray([], declaration.roles, true),
96
+ secretName: declaration.secretName,
97
+ status: booted ? (row.status === 'deactivated' ? 'deactivated' : 'active') : 'pending first boot',
98
+ hasCredential: booted && !!row.password,
99
+ });
100
+ _b.label = 3;
101
+ case 3:
102
+ _i++;
103
+ return [3 /*break*/, 1];
104
+ case 4: return [2 /*return*/, views];
105
+ }
106
+ });
107
+ });
108
+ };
109
+ MachineCredentials.prototype.mintCredential = function (email) {
110
+ return __awaiter(this, void 0, void 0, function () {
111
+ var logger, normalizedEmail, declaration, db, user, password, _a, _b, _c;
112
+ var _d;
113
+ return __generator(this, function (_e) {
114
+ switch (_e.label) {
115
+ case 0:
116
+ logger = new logger_1.Logger({ name: 'MachineCredentials.mintCredential' });
117
+ normalizedEmail = email === null || email === void 0 ? void 0 : email.toLowerCase();
118
+ declaration = (0, user_1.getMachineAccounts)().find(function (machineAccount) { return machineAccount.email === normalizedEmail; });
119
+ if (!declaration) {
120
+ throw new Error("No machine account is declared for '".concat(email, "'. Credentials can only be minted for ") +
121
+ "code-declared machine accounts (MachineAccount declarations).");
122
+ }
123
+ db = (0, db_1.getDbAsSystem)();
124
+ return [4 /*yield*/, db.get(user_1.tables.User, { email: normalizedEmail })];
125
+ case 1:
126
+ user = _e.sent();
127
+ if (!user || user.isLoadedFromSource !== true) {
128
+ throw new Error("The machine account row for '".concat(normalizedEmail, "' has not been loaded from source yet \u2014 ") +
129
+ "the boot sync creates (or adopts) it. Boot the server with the declaration, then mint.");
130
+ }
131
+ password = (0, crypto_1.randomBytes)(32).toString('hex');
132
+ _b = (_a = db).update;
133
+ _c = [user_1.tables.User];
134
+ _d = { id: user.id };
135
+ return [4 /*yield*/, new PasswordHasher_1.PasswordHasher().hash(password, 'machine')];
136
+ case 2: return [4 /*yield*/, _b.apply(_a, _c.concat([(_d.password = _e.sent(), _d)]))];
137
+ case 3:
138
+ _e.sent();
139
+ // Rotation kills the old credential's sessions immediately (the bridge logs in fresh per
140
+ // poll, so this is cheap for it — the mechanism is the categorical one).
141
+ return [4 /*yield*/, db.delete(user_1.tables.Session, { userEmail: normalizedEmail })];
142
+ case 4:
143
+ // Rotation kills the old credential's sessions immediately (the bridge logs in fresh per
144
+ // poll, so this is cheap for it — the mechanism is the categorical one).
145
+ _e.sent();
146
+ logger.info({
147
+ message: "Minted machine credential",
148
+ obj: { email: normalizedEmail, secretName: declaration.secretName },
149
+ });
150
+ return [2 /*return*/, {
151
+ email: normalizedEmail,
152
+ password: password,
153
+ secretName: declaration.secretName,
154
+ note: "Shown once. Paste this password into the '".concat(declaration.secretName, "' Secret Manager ") +
155
+ "secret, then restart the service that reads it \u2014 the previous credential is already invalid.",
156
+ }];
157
+ }
158
+ });
159
+ });
160
+ };
161
+ return MachineCredentials;
162
+ }());
163
+ exports.MachineCredentials = MachineCredentials;
164
+ //# sourceMappingURL=MachineCredentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MachineCredentials.js","sourceRoot":"","sources":["../../../src/services/MachineCredentials.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Roles.d.ts","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAoD,MAAM,iBAAiB,CAAC;AAEjG,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,KAAM,YAAW,YAAY;IACjC,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAIhD;IAEI,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAI/C,UAAU;CAkCzB"}
1
+ {"version":3,"file":"Roles.d.ts","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAoD,MAAM,iBAAiB,CAAC;AAEjG,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,KAAM,YAAW,YAAY;IACjC,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAIhD;IAEI,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAI/C,UAAU;CA4CzB"}