@proteinjs/user-server 1.22.1 → 1.22.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 (152) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/generated/index.js +1 -1
  3. package/dist/generated/index.js.map +1 -1
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +9 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/src/authentication/PasswordHasher.d.ts +9 -0
  9. package/dist/src/authentication/PasswordHasher.d.ts.map +1 -1
  10. package/dist/src/authentication/PasswordHasher.js +25 -0
  11. package/dist/src/authentication/PasswordHasher.js.map +1 -1
  12. package/dist/src/authentication/SocketIOSessionWatcher.d.ts +5 -0
  13. package/dist/src/authentication/SocketIOSessionWatcher.d.ts.map +1 -1
  14. package/dist/src/authentication/SocketIOSessionWatcher.js +13 -2
  15. package/dist/src/authentication/SocketIOSessionWatcher.js.map +1 -1
  16. package/dist/src/authentication/UserStatusTableWatcher.d.ts.map +1 -1
  17. package/dist/src/authentication/UserStatusTableWatcher.js +2 -1
  18. package/dist/src/authentication/UserStatusTableWatcher.js.map +1 -1
  19. package/dist/src/authentication/authenticate.d.ts +15 -0
  20. package/dist/src/authentication/authenticate.d.ts.map +1 -1
  21. package/dist/src/authentication/authenticate.js +55 -20
  22. package/dist/src/authentication/authenticate.js.map +1 -1
  23. package/dist/src/authorization/userCache.d.ts.map +1 -1
  24. package/dist/src/authorization/userCache.js +3 -2
  25. package/dist/src/authorization/userCache.js.map +1 -1
  26. package/dist/src/routes/devLogin.d.ts.map +1 -1
  27. package/dist/src/routes/devLogin.js +29 -14
  28. package/dist/src/routes/devLogin.js.map +1 -1
  29. package/dist/src/routes/executePasswordReset.d.ts +4 -0
  30. package/dist/src/routes/executePasswordReset.d.ts.map +1 -1
  31. package/dist/src/routes/executePasswordReset.js +12 -4
  32. package/dist/src/routes/executePasswordReset.js.map +1 -1
  33. package/dist/src/routes/initiatePasswordReset.d.ts +7 -0
  34. package/dist/src/routes/initiatePasswordReset.d.ts.map +1 -1
  35. package/dist/src/routes/initiatePasswordReset.js +28 -10
  36. package/dist/src/routes/initiatePasswordReset.js.map +1 -1
  37. package/dist/src/routes/login.d.ts +8 -0
  38. package/dist/src/routes/login.d.ts.map +1 -1
  39. package/dist/src/routes/login.js +63 -22
  40. package/dist/src/routes/login.js.map +1 -1
  41. package/dist/src/routes/signup.d.ts.map +1 -1
  42. package/dist/src/routes/signup.js +5 -2
  43. package/dist/src/routes/signup.js.map +1 -1
  44. package/dist/src/routes/validateResetPasswordToken.d.ts.map +1 -1
  45. package/dist/src/routes/validateResetPasswordToken.js +5 -1
  46. package/dist/src/routes/validateResetPasswordToken.js.map +1 -1
  47. package/dist/src/services/MachineCredentials.d.ts +10 -1
  48. package/dist/src/services/MachineCredentials.d.ts.map +1 -1
  49. package/dist/src/services/MachineCredentials.js +46 -15
  50. package/dist/src/services/MachineCredentials.js.map +1 -1
  51. package/dist/src/services/Roles.d.ts.map +1 -1
  52. package/dist/src/services/Roles.js +6 -2
  53. package/dist/src/services/Roles.js.map +1 -1
  54. package/dist/src/services/Signup.d.ts +5 -0
  55. package/dist/src/services/Signup.d.ts.map +1 -1
  56. package/dist/src/services/Signup.js +28 -6
  57. package/dist/src/services/Signup.js.map +1 -1
  58. package/dist/src/throttle/ClientAddress.d.ts +35 -0
  59. package/dist/src/throttle/ClientAddress.d.ts.map +1 -0
  60. package/dist/src/throttle/ClientAddress.js +48 -0
  61. package/dist/src/throttle/ClientAddress.js.map +1 -0
  62. package/dist/src/throttle/PasswordResetThrottle.d.ts +39 -0
  63. package/dist/src/throttle/PasswordResetThrottle.d.ts.map +1 -0
  64. package/dist/src/throttle/PasswordResetThrottle.js +59 -0
  65. package/dist/src/throttle/PasswordResetThrottle.js.map +1 -0
  66. package/dist/src/throttle/RequestDigests.d.ts +42 -0
  67. package/dist/src/throttle/RequestDigests.d.ts.map +1 -0
  68. package/dist/src/throttle/RequestDigests.js +96 -0
  69. package/dist/src/throttle/RequestDigests.js.map +1 -0
  70. package/dist/src/throttle/SignInThrottle.d.ts +67 -0
  71. package/dist/src/throttle/SignInThrottle.d.ts.map +1 -0
  72. package/dist/src/throttle/SignInThrottle.js +91 -0
  73. package/dist/src/throttle/SignInThrottle.js.map +1 -0
  74. package/dist/src/throttle/SlidingWindow.d.ts +51 -0
  75. package/dist/src/throttle/SlidingWindow.d.ts.map +1 -0
  76. package/dist/src/throttle/SlidingWindow.js +85 -0
  77. package/dist/src/throttle/SlidingWindow.js.map +1 -0
  78. package/dist/test/ClientAddress.test.d.ts +2 -0
  79. package/dist/test/ClientAddress.test.d.ts.map +1 -0
  80. package/dist/test/ClientAddress.test.js +51 -0
  81. package/dist/test/ClientAddress.test.js.map +1 -0
  82. package/dist/test/DevLogin.test.js +68 -0
  83. package/dist/test/DevLogin.test.js.map +1 -1
  84. package/dist/test/InitiatePasswordReset.test.js +373 -5
  85. package/dist/test/InitiatePasswordReset.test.js.map +1 -1
  86. package/dist/test/LogCapture.d.ts +10 -0
  87. package/dist/test/LogCapture.d.ts.map +1 -1
  88. package/dist/test/LogCapture.js +19 -0
  89. package/dist/test/LogCapture.js.map +1 -1
  90. package/dist/test/LogCapture.test.d.ts +2 -0
  91. package/dist/test/LogCapture.test.d.ts.map +1 -0
  92. package/dist/test/LogCapture.test.js +86 -0
  93. package/dist/test/LogCapture.test.js.map +1 -0
  94. package/dist/test/LoginThrottle.integration.test.d.ts +2 -0
  95. package/dist/test/LoginThrottle.integration.test.d.ts.map +1 -0
  96. package/dist/test/LoginThrottle.integration.test.js +616 -0
  97. package/dist/test/LoginThrottle.integration.test.js.map +1 -0
  98. package/dist/test/MachineAccounts.integration.test.js +156 -28
  99. package/dist/test/MachineAccounts.integration.test.js.map +1 -1
  100. package/dist/test/NoAddressesInLogs.integration.test.d.ts +2 -0
  101. package/dist/test/NoAddressesInLogs.integration.test.d.ts.map +1 -0
  102. package/dist/test/NoAddressesInLogs.integration.test.js +717 -0
  103. package/dist/test/NoAddressesInLogs.integration.test.js.map +1 -0
  104. package/dist/test/RequestDigests.test.d.ts +2 -0
  105. package/dist/test/RequestDigests.test.d.ts.map +1 -0
  106. package/dist/test/RequestDigests.test.js +73 -0
  107. package/dist/test/RequestDigests.test.js.map +1 -0
  108. package/dist/test/SignupRoute.test.js +80 -1
  109. package/dist/test/SignupRoute.test.js.map +1 -1
  110. package/dist/test/SlidingWindow.test.d.ts +2 -0
  111. package/dist/test/SlidingWindow.test.d.ts.map +1 -0
  112. package/dist/test/SlidingWindow.test.js +72 -0
  113. package/dist/test/SlidingWindow.test.js.map +1 -0
  114. package/dist/test/SocketIOSessionWatcher.test.d.ts +2 -0
  115. package/dist/test/SocketIOSessionWatcher.test.d.ts.map +1 -0
  116. package/dist/test/SocketIOSessionWatcher.test.js +144 -0
  117. package/dist/test/SocketIOSessionWatcher.test.js.map +1 -0
  118. package/generated/index.ts +1 -1
  119. package/index.ts +5 -0
  120. package/package.json +6 -5
  121. package/src/authentication/PasswordHasher.ts +18 -1
  122. package/src/authentication/SocketIOSessionWatcher.ts +13 -1
  123. package/src/authentication/UserStatusTableWatcher.ts +2 -1
  124. package/src/authentication/authenticate.ts +36 -9
  125. package/src/authorization/userCache.ts +3 -2
  126. package/src/routes/devLogin.ts +22 -10
  127. package/src/routes/executePasswordReset.ts +11 -3
  128. package/src/routes/initiatePasswordReset.ts +29 -11
  129. package/src/routes/login.ts +39 -8
  130. package/src/routes/signup.ts +5 -2
  131. package/src/routes/validateResetPasswordToken.ts +5 -1
  132. package/src/services/MachineCredentials.ts +44 -9
  133. package/src/services/Roles.ts +6 -2
  134. package/src/services/Signup.ts +30 -4
  135. package/src/throttle/ClientAddress.ts +47 -0
  136. package/src/throttle/PasswordResetThrottle.ts +62 -0
  137. package/src/throttle/RequestDigests.ts +97 -0
  138. package/src/throttle/SignInThrottle.ts +98 -0
  139. package/src/throttle/SlidingWindow.ts +90 -0
  140. package/test/ClientAddress.test.ts +54 -0
  141. package/test/DevLogin.test.ts +35 -1
  142. package/test/InitiatePasswordReset.test.ts +212 -6
  143. package/test/LogCapture.test.ts +29 -0
  144. package/test/LogCapture.ts +16 -0
  145. package/test/LoginThrottle.integration.test.ts +339 -0
  146. package/test/MachineAccounts.integration.test.ts +97 -22
  147. package/test/NoAddressesInLogs.integration.test.ts +409 -0
  148. package/test/RequestDigests.test.ts +81 -0
  149. package/test/SignupRoute.test.ts +54 -1
  150. package/test/SlidingWindow.test.ts +85 -0
  151. package/test/SocketIOSessionWatcher.test.ts +86 -0
  152. package/test/setup.js +2 -0
@@ -0,0 +1,717 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __assign = (this && this.__assign) || function () {
18
+ __assign = Object.assign || function(t) {
19
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
20
+ s = arguments[i];
21
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
+ t[p] = s[p];
23
+ }
24
+ return t;
25
+ };
26
+ return __assign.apply(this, arguments);
27
+ };
28
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
+ return new (P || (P = Promise))(function (resolve, reject) {
31
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
32
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
33
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
34
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
35
+ });
36
+ };
37
+ var __generator = (this && this.__generator) || function (thisArg, body) {
38
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
39
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
+ function verb(n) { return function (v) { return step([n, v]); }; }
41
+ function step(op) {
42
+ if (f) throw new TypeError("Generator is already executing.");
43
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
44
+ 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;
45
+ if (y = 0, t) op = [op[0] & 2, t.value];
46
+ switch (op[0]) {
47
+ case 0: case 1: t = op; break;
48
+ case 4: _.label++; return { value: op[1], done: false };
49
+ case 5: _.label++; y = op[1]; op = [0]; continue;
50
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
51
+ default:
52
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
53
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
54
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
55
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
56
+ if (t[2]) _.ops.pop();
57
+ _.trys.pop(); continue;
58
+ }
59
+ op = body.call(thisArg, _);
60
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
61
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
62
+ }
63
+ };
64
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
65
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
66
+ if (ar || !(i in from)) {
67
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
68
+ ar[i] = from[i];
69
+ }
70
+ }
71
+ return to.concat(ar || Array.prototype.slice.call(from));
72
+ };
73
+ var __importDefault = (this && this.__importDefault) || function (mod) {
74
+ return (mod && mod.__esModule) ? mod : { "default": mod };
75
+ };
76
+ Object.defineProperty(exports, "__esModule", { value: true });
77
+ var moment_1 = __importDefault(require("moment"));
78
+ var sha256_1 = __importDefault(require("crypto-js/sha256"));
79
+ var db_1 = require("@proteinjs/db");
80
+ var email_server_1 = require("@proteinjs/email-server");
81
+ var reflection_1 = require("@proteinjs/reflection");
82
+ var user_1 = require("@proteinjs/user");
83
+ var util_node_1 = require("@proteinjs/util-node");
84
+ var authenticate_1 = require("../src/authentication/authenticate");
85
+ var PasswordHasher_1 = require("../src/authentication/PasswordHasher");
86
+ var PasswordResetToken_1 = require("../src/authentication/PasswordResetToken");
87
+ var UserStatusTableWatcher_1 = require("../src/authentication/UserStatusTableWatcher");
88
+ var userCache_1 = require("../src/authorization/userCache");
89
+ var executePasswordReset_1 = require("../src/routes/executePasswordReset");
90
+ var initiatePasswordReset_1 = require("../src/routes/initiatePasswordReset");
91
+ var validateResetPasswordToken_1 = require("../src/routes/validateResetPasswordToken");
92
+ var signup_1 = require("../src/routes/signup");
93
+ var MachineCredentials_1 = require("../src/services/MachineCredentials");
94
+ var Signup_1 = require("../src/services/Signup");
95
+ var devLoginHarness_1 = require("./devLoginHarness");
96
+ var passportSessionHarness_1 = require("./passportSessionHarness");
97
+ var LogCapture_1 = require("./LogCapture");
98
+ var UserServerTestEnvironment_1 = require("./UserServerTestEnvironment");
99
+ var testEnv = new UserServerTestEnvironment_1.UserServerTestEnvironment();
100
+ var invoke = function (route, request) { return __awaiter(void 0, void 0, void 0, function () {
101
+ var outcome, response;
102
+ return __generator(this, function (_a) {
103
+ switch (_a.label) {
104
+ case 0:
105
+ outcome = { status: 200 };
106
+ response = {
107
+ status: function (code) {
108
+ outcome.status = code;
109
+ return this;
110
+ },
111
+ send: function (body) {
112
+ outcome.body = body;
113
+ },
114
+ };
115
+ return [4 /*yield*/, route.onRequest(request, response)];
116
+ case 1:
117
+ _a.sent();
118
+ return [2 /*return*/, outcome];
119
+ }
120
+ });
121
+ }); };
122
+ var objectCache = function () { return reflection_1.SourceRepository.get().objectCache; };
123
+ var namedObjectCache = function () { return reflection_1.SourceRepository.get().namedObjectCache; };
124
+ /** The deactivation watcher map is built once per process from the cache; rebuild it from what is seeded now. */
125
+ var resetTableWatcherMap = function () {
126
+ var runner = (0, db_1.getDbAsSystem)().tableWatcherRunner;
127
+ runner.constructor.tableWatcherMap = undefined;
128
+ };
129
+ var OpsRole = /** @class */ (function () {
130
+ function OpsRole() {
131
+ this.role = 'ops';
132
+ this.description = 'Operational machinery';
133
+ }
134
+ return OpsRole;
135
+ }());
136
+ var LogsMachineAccount = /** @class */ (function (_super) {
137
+ __extends(LogsMachineAccount, _super);
138
+ function LogsMachineAccount() {
139
+ var _this = _super !== null && _super.apply(this, arguments) || this;
140
+ _this.id = 'machine-logs-ops';
141
+ _this.email = 'machine-logs@test.local';
142
+ _this.accountName = 'Logs ops machine';
143
+ _this.roles = ['ops'];
144
+ _this.secretName = 'logs-ops-secret';
145
+ return _this;
146
+ }
147
+ return LogsMachineAccount;
148
+ }(user_1.MachineAccount));
149
+ var DEV_ENV = ['DEVELOPMENT', 'DEV_AUTO_LOGIN_EMAIL', 'DEV_BOOTSTRAP_ADMIN_EMAIL', 'DEV_BOOTSTRAP_ROLES'];
150
+ describe('no e-mail address reaches the server log', function () {
151
+ var digests = new util_node_1.RequestDigests();
152
+ var originalEnv = {};
153
+ beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
154
+ var _i, _a, key, cache;
155
+ return __generator(this, function (_b) {
156
+ switch (_b.label) {
157
+ case 0: return [4 /*yield*/, testEnv.beforeAll()];
158
+ case 1:
159
+ _b.sent();
160
+ for (_i = 0, _a = __spreadArray(['EMAIL_TRANSPORT'], DEV_ENV, true); _i < _a.length; _i++) {
161
+ key = _a[_i];
162
+ originalEnv[key] = process.env[key];
163
+ }
164
+ process.env.EMAIL_TRANSPORT = 'sink';
165
+ cache = objectCache();
166
+ cache['@proteinjs/email-server/DefaultEmailConfigFactory'] = [
167
+ {
168
+ getEmailConfig: function () { return ({
169
+ host: 'smtp.test.local',
170
+ port: 465,
171
+ secure: true,
172
+ from: '"Example" <hi@example.com>',
173
+ }); },
174
+ },
175
+ ];
176
+ cache['@proteinjs/email-server/DefaultPasswordResetEmailConfigFactory'] = [
177
+ { getConfig: function () { return ({ getEmailContent: function (path) { return ({ text: "Reset: https://app.test.local/".concat(path) }); } }); } },
178
+ ];
179
+ cache['@proteinjs/email-server/DefaultInviteEmailConfigFactory'] = [
180
+ {
181
+ getConfig: function () { return ({
182
+ options: { subject: 'Your invite' },
183
+ getEmailContent: function (path) { return ({ text: "Accept: /".concat(path), html: "<a href=\"/".concat(path, "\">Accept</a>") }); },
184
+ }); },
185
+ },
186
+ ];
187
+ cache['@proteinjs/email-server/DefaultSignupConfirmationEmailConfigFactory'] = [
188
+ { getConfig: function () { return ({ getNewUserEmailContent: function () { return ({ text: 'welcome', html: '<p>welcome</p>' }); } }); } },
189
+ ];
190
+ // Invite-optional, the library default — the suites load src, never the generated source graph.
191
+ cache['@proteinjs/user-server/DefaultInviteConfigFactory'] = [{ getConfig: function () { return ({ isInviteOnly: false }); } }];
192
+ cache['@proteinjs/user-auth/AuthenticatedUserRepo'] = [new user_1.UserRepo()];
193
+ cache['@proteinjs/user/RoleCatalogEntry'] = [new OpsRole()];
194
+ cache['@proteinjs/db/TableWatcher'] = [new UserStatusTableWatcher_1.UserStatusTableWatcher()];
195
+ resetTableWatcherMap();
196
+ return [2 /*return*/];
197
+ }
198
+ });
199
+ }); }, 120000);
200
+ afterAll(function () { return __awaiter(void 0, void 0, void 0, function () {
201
+ var cache, _i, _a, key, _b, _c, _d, key, value;
202
+ return __generator(this, function (_e) {
203
+ switch (_e.label) {
204
+ case 0:
205
+ cache = objectCache();
206
+ for (_i = 0, _a = [
207
+ '@proteinjs/email-server/DefaultEmailConfigFactory',
208
+ '@proteinjs/email-server/DefaultPasswordResetEmailConfigFactory',
209
+ '@proteinjs/email-server/DefaultInviteEmailConfigFactory',
210
+ '@proteinjs/email-server/DefaultSignupConfirmationEmailConfigFactory',
211
+ '@proteinjs/user-server/DefaultInviteConfigFactory',
212
+ '@proteinjs/user-auth/AuthenticatedUserRepo',
213
+ '@proteinjs/user/RoleCatalogEntry',
214
+ '@proteinjs/db/TableWatcher',
215
+ '@proteinjs/db/SourceRecordLoader',
216
+ ]; _i < _a.length; _i++) {
217
+ key = _a[_i];
218
+ delete cache[key];
219
+ }
220
+ delete namedObjectCache()['@proteinjs/db/SourceRecordLoader'];
221
+ resetTableWatcherMap();
222
+ for (_b = 0, _c = Object.entries(originalEnv); _b < _c.length; _b++) {
223
+ _d = _c[_b], key = _d[0], value = _d[1];
224
+ if (value === undefined) {
225
+ delete process.env[key];
226
+ }
227
+ else {
228
+ process.env[key] = value;
229
+ }
230
+ }
231
+ return [4 /*yield*/, testEnv.afterAll()];
232
+ case 1:
233
+ _e.sent();
234
+ return [2 /*return*/];
235
+ }
236
+ });
237
+ }); });
238
+ beforeEach(function () {
239
+ email_server_1.MailSink.get().clear();
240
+ });
241
+ it('the reset door: an unknown address, a mailed link, a repeat inside five minutes, a failed send', function () { return __awaiter(void 0, void 0, void 0, function () {
242
+ var log, _i, _a, address;
243
+ return __generator(this, function (_b) {
244
+ switch (_b.label) {
245
+ case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Reset Mailed', email: 'reset-mailed@test.local' })];
246
+ case 1:
247
+ _b.sent();
248
+ return [4 /*yield*/, testEnv.createUser({ name: 'Reset Fails', email: 'reset-fails@test.local' })];
249
+ case 2:
250
+ _b.sent();
251
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
252
+ var failing;
253
+ return __generator(this, function (_a) {
254
+ switch (_a.label) {
255
+ case 0: return [4 /*yield*/, invoke(initiatePasswordReset_1.initiatePasswordReset, { body: { email: 'reset-nobody@test.local' } })];
256
+ case 1:
257
+ _a.sent();
258
+ return [4 /*yield*/, invoke(initiatePasswordReset_1.initiatePasswordReset, { body: { email: 'Reset-Mailed@test.local' } })];
259
+ case 2:
260
+ _a.sent();
261
+ return [4 /*yield*/, invoke(initiatePasswordReset_1.initiatePasswordReset, { body: { email: 'reset-mailed@test.local' } })];
262
+ case 3:
263
+ _a.sent();
264
+ failing = jest
265
+ .spyOn(email_server_1.EmailSender.prototype, 'sendEmail')
266
+ .mockRejectedValueOnce(new Error('Failed to send email'));
267
+ _a.label = 4;
268
+ case 4:
269
+ _a.trys.push([4, , 6, 7]);
270
+ return [4 /*yield*/, invoke(initiatePasswordReset_1.initiatePasswordReset, { body: { email: 'reset-fails@test.local' } })];
271
+ case 5:
272
+ _a.sent();
273
+ return [3 /*break*/, 7];
274
+ case 6:
275
+ failing.mockRestore();
276
+ return [7 /*endfinally*/];
277
+ case 7: return [2 /*return*/];
278
+ }
279
+ });
280
+ }); })];
281
+ case 3:
282
+ log = _b.sent();
283
+ expect(email_server_1.MailSink.get().list()).toHaveLength(1);
284
+ expect(log.addresses).toEqual([]);
285
+ for (_i = 0, _a = ['reset-nobody@test.local', 'reset-mailed@test.local', 'reset-fails@test.local']; _i < _a.length; _i++) {
286
+ address = _a[_i];
287
+ expect(log.text).toContain(digests.account(address));
288
+ }
289
+ return [2 /*return*/];
290
+ }
291
+ });
292
+ }); });
293
+ it('the reset link: redeemed, presented again, expired, and an expired link validated', function () { return __awaiter(void 0, void 0, void 0, function () {
294
+ var redeemer, token, lapsed, lapsedToken, log;
295
+ return __generator(this, function (_a) {
296
+ switch (_a.label) {
297
+ case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Reset Redeemer', email: 'reset-redeem@test.local' })];
298
+ case 1:
299
+ redeemer = _a.sent();
300
+ return [4 /*yield*/, new PasswordResetToken_1.PasswordResetToken().mint(redeemer)];
301
+ case 2:
302
+ token = _a.sent();
303
+ return [4 /*yield*/, testEnv.createUser({ name: 'Reset Lapsed', email: 'reset-lapsed@test.local' })];
304
+ case 3:
305
+ lapsed = _a.sent();
306
+ return [4 /*yield*/, new PasswordResetToken_1.PasswordResetToken().mint(lapsed)];
307
+ case 4:
308
+ lapsedToken = _a.sent();
309
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().update(user_1.tables.User, {
310
+ id: lapsed.id,
311
+ passwordResetTokenExpiration: (0, moment_1.default)().subtract(1, 'hour'),
312
+ })];
313
+ case 5:
314
+ _a.sent();
315
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
316
+ var _a, _b, _c, _d;
317
+ return __generator(this, function (_e) {
318
+ switch (_e.label) {
319
+ case 0:
320
+ _a = expect;
321
+ return [4 /*yield*/, invoke(executePasswordReset_1.executePasswordReset, { body: { token: token, newPassword: 'fresh-password' } })];
322
+ case 1:
323
+ _a.apply(void 0, [(_e.sent()).status]).toBe(200);
324
+ _b = expect;
325
+ return [4 /*yield*/, invoke(executePasswordReset_1.executePasswordReset, { body: { token: token, newPassword: 'again-password' } })];
326
+ case 2:
327
+ _b.apply(void 0, [(_e.sent()).status]).toBe(400);
328
+ _c = expect;
329
+ return [4 /*yield*/, invoke(executePasswordReset_1.executePasswordReset, { body: { token: lapsedToken, newPassword: 'late-password' } })];
330
+ case 3:
331
+ _c.apply(void 0, [(_e.sent()).status]).toBe(400);
332
+ _d = expect;
333
+ return [4 /*yield*/, invoke(validateResetPasswordToken_1.validateResetPasswordToken, { query: { token: lapsedToken } })];
334
+ case 4:
335
+ _d.apply(void 0, [(_e.sent()).body.isValid]).toBe(false);
336
+ return [2 /*return*/];
337
+ }
338
+ });
339
+ }); })];
340
+ case 6:
341
+ log = _a.sent();
342
+ expect(log.addresses).toEqual([]);
343
+ expect(log.text).toContain(digests.account('reset-redeem@test.local'));
344
+ expect(log.text).toContain(digests.account('reset-lapsed@test.local'));
345
+ return [2 /*return*/];
346
+ }
347
+ });
348
+ }); });
349
+ it('an invite: sent, re-sent, a send that fails, a re-send that fails — the invitee by its address digest', function () { return __awaiter(void 0, void 0, void 0, function () {
350
+ var inviter, log;
351
+ return __generator(this, function (_a) {
352
+ switch (_a.label) {
353
+ case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Inviter', email: 'logs-inviter@test.local', roles: ['admin'] })];
354
+ case 1:
355
+ inviter = _a.sent();
356
+ testEnv.actAs(inviter);
357
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
358
+ var _a, _b, failing, _c, _d;
359
+ return __generator(this, function (_e) {
360
+ switch (_e.label) {
361
+ case 0:
362
+ _a = expect;
363
+ return [4 /*yield*/, new Signup_1.Signup().sendInvite('Invitee@Example.invalid')];
364
+ case 1:
365
+ _a.apply(void 0, [_e.sent()]).toEqual({ sent: true });
366
+ _b = expect;
367
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite('invitee@example.invalid')];
368
+ case 2:
369
+ _b.apply(void 0, [_e.sent()]).toEqual({ sent: true });
370
+ failing = jest
371
+ .spyOn(email_server_1.EmailSender.prototype, 'sendEmail')
372
+ .mockRejectedValue(new Error('Failed to send email'));
373
+ _e.label = 3;
374
+ case 3:
375
+ _e.trys.push([3, , 6, 7]);
376
+ _c = expect;
377
+ return [4 /*yield*/, new Signup_1.Signup().sendInvite('invitee-fails@example.invalid')];
378
+ case 4:
379
+ _c.apply(void 0, [(_e.sent()).sent]).toBe(false);
380
+ _d = expect;
381
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite('invitee@example.invalid')];
382
+ case 5:
383
+ _d.apply(void 0, [(_e.sent()).sent]).toBe(false);
384
+ return [3 /*break*/, 7];
385
+ case 6:
386
+ failing.mockRestore();
387
+ return [7 /*endfinally*/];
388
+ case 7: return [2 /*return*/];
389
+ }
390
+ });
391
+ }); })];
392
+ case 2:
393
+ log = _a.sent();
394
+ expect(email_server_1.MailSink.get().list()).toHaveLength(2);
395
+ expect(log.addresses).toEqual([]);
396
+ expect(log.text).toContain(digests.address('invitee-fails@example.invalid'));
397
+ expect(log.text).toContain(digests.address('invitee@example.invalid'));
398
+ return [2 /*return*/];
399
+ }
400
+ });
401
+ }); });
402
+ it('signup: an account created, then the same address again', function () { return __awaiter(void 0, void 0, void 0, function () {
403
+ var log;
404
+ return __generator(this, function (_a) {
405
+ switch (_a.label) {
406
+ case 0: return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
407
+ var user, _a, _b;
408
+ return __generator(this, function (_c) {
409
+ switch (_c.label) {
410
+ case 0:
411
+ user = { name: 'Signer', email: 'Logs-Signup@test.local', password: 'a-password' };
412
+ _a = expect;
413
+ return [4 /*yield*/, new Signup_1.Signup().createUser(user)];
414
+ case 1:
415
+ _a.apply(void 0, [(_c.sent()).outcome]).toBe('created');
416
+ _b = expect;
417
+ return [4 /*yield*/, new Signup_1.Signup().createUser(user)];
418
+ case 2:
419
+ _b.apply(void 0, [(_c.sent()).outcome]).toBe('exists');
420
+ return [2 /*return*/];
421
+ }
422
+ });
423
+ }); })];
424
+ case 1:
425
+ log = _a.sent();
426
+ expect(log.addresses).toEqual([]);
427
+ expect(log.text).toContain(digests.account('logs-signup@test.local'));
428
+ return [2 /*return*/];
429
+ }
430
+ });
431
+ }); });
432
+ it("a double sign-up race: the database's refusal names the address — the log carries its digest instead", function () { return __awaiter(void 0, void 0, void 0, function () {
433
+ var hash, arrived, bothArrived, both, held, body, outcomes, log, _a, refusal;
434
+ return __generator(this, function (_b) {
435
+ switch (_b.label) {
436
+ case 0:
437
+ hash = PasswordHasher_1.PasswordHasher.prototype.hash;
438
+ arrived = 0;
439
+ both = new Promise(function (resolve) { return (bothArrived = resolve); });
440
+ held = jest.spyOn(PasswordHasher_1.PasswordHasher.prototype, 'hash').mockImplementation(function (password) {
441
+ return __awaiter(this, void 0, void 0, function () {
442
+ var hashed;
443
+ return __generator(this, function (_a) {
444
+ switch (_a.label) {
445
+ case 0: return [4 /*yield*/, hash.call(this, password)];
446
+ case 1:
447
+ hashed = _a.sent();
448
+ if (++arrived === 2) {
449
+ bothArrived();
450
+ }
451
+ return [4 /*yield*/, both];
452
+ case 2:
453
+ _a.sent();
454
+ return [2 /*return*/, hashed];
455
+ }
456
+ });
457
+ });
458
+ });
459
+ body = { name: 'Racer', email: 'Logs-Race@test.local', password: 'a-password' };
460
+ outcomes = [];
461
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
462
+ return __generator(this, function (_a) {
463
+ switch (_a.label) {
464
+ case 0:
465
+ _a.trys.push([0, , 2, 3]);
466
+ return [4 /*yield*/, Promise.all([0, 1].map(function () { return __awaiter(void 0, void 0, void 0, function () {
467
+ var request, outcome;
468
+ var _a;
469
+ return __generator(this, function (_b) {
470
+ switch (_b.label) {
471
+ case 0: return [4 /*yield*/, (0, passportSessionHarness_1.createPassportRequest)({ body: body })];
472
+ case 1:
473
+ request = (_b.sent()).request;
474
+ return [4 /*yield*/, invoke(signup_1.signup, request)];
475
+ case 2:
476
+ outcome = _b.sent();
477
+ return [2 /*return*/, __assign(__assign({}, outcome), { loggedInAs: (_a = request.session.passport) === null || _a === void 0 ? void 0 : _a.user })];
478
+ }
479
+ });
480
+ }); }))];
481
+ case 1:
482
+ outcomes = _a.sent();
483
+ return [3 /*break*/, 3];
484
+ case 2:
485
+ held.mockRestore();
486
+ return [7 /*endfinally*/];
487
+ case 3: return [2 /*return*/];
488
+ }
489
+ });
490
+ }); })];
491
+ case 1:
492
+ log = _b.sent();
493
+ // One account, one session; the loser was refused by the index and logged it — the refusal
494
+ // naming the index, the address in its words replaced by the address's digest.
495
+ _a = expect;
496
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.User, { email: 'logs-race@test.local' })];
497
+ case 2:
498
+ // One account, one session; the loser was refused by the index and logged it — the refusal
499
+ // naming the index, the address in its words replaced by the address's digest.
500
+ _a.apply(void 0, [(_b.sent()).length]).toBe(1);
501
+ expect(outcomes.filter(function (outcome) { return outcome.loggedInAs === 'logs-race@test.local'; })).toHaveLength(1);
502
+ refusal = log.linesContaining('Signup failed');
503
+ expect(refusal.lines).toHaveLength(1);
504
+ expect(refusal.text).toContain('user_email_unique');
505
+ expect(refusal.addresses).toEqual([]);
506
+ expect(refusal.text).toContain(digests.address('logs-race@test.local'));
507
+ // The database driver's own line for the refused statement is the driver's (the version a
508
+ // package lock pins decides whether it prints bound values); this door's lines are the ones above.
509
+ expect(log.linesContaining('[signup]').addresses).toEqual([]);
510
+ return [2 /*return*/];
511
+ }
512
+ });
513
+ }); });
514
+ it('an invite whose send or write fails with the address in its error: the invitee by its digest, the error too', function () { return __awaiter(void 0, void 0, void 0, function () {
515
+ var inviter, naming, log;
516
+ return __generator(this, function (_a) {
517
+ switch (_a.label) {
518
+ case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Inviter', email: 'logs-inviter-2@test.local', roles: ['admin'] })];
519
+ case 1:
520
+ inviter = _a.sent();
521
+ testEnv.actAs(inviter);
522
+ return [4 /*yield*/, new Signup_1.Signup().sendInvite('invitee-standing@example.invalid')];
523
+ case 2:
524
+ _a.sent();
525
+ naming = function (address) {
526
+ return Object.assign(new Error("6 ALREADY_EXISTS: Row [".concat(address, "] in table Invite already exists")), {
527
+ code: 6,
528
+ details: "Row [".concat(address, "] in table Invite already exists"),
529
+ });
530
+ };
531
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
532
+ var failing, _a, _b;
533
+ return __generator(this, function (_c) {
534
+ switch (_c.label) {
535
+ case 0:
536
+ failing = jest
537
+ .spyOn(email_server_1.EmailSender.prototype, 'sendEmail')
538
+ .mockImplementation(function (options) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
539
+ return [2 /*return*/, Promise.reject(naming(String(options.to)))];
540
+ }); }); });
541
+ _c.label = 1;
542
+ case 1:
543
+ _c.trys.push([1, , 4, 5]);
544
+ _a = expect;
545
+ return [4 /*yield*/, new Signup_1.Signup().sendInvite('Invitee-Named@Example.invalid')];
546
+ case 2:
547
+ _a.apply(void 0, [(_c.sent()).sent]).toBe(false);
548
+ _b = expect;
549
+ return [4 /*yield*/, new Signup_1.Signup().resendInvite('invitee-standing@example.invalid')];
550
+ case 3:
551
+ _b.apply(void 0, [(_c.sent()).sent]).toBe(false);
552
+ return [3 /*break*/, 5];
553
+ case 4:
554
+ failing.mockRestore();
555
+ return [7 /*endfinally*/];
556
+ case 5: return [2 /*return*/];
557
+ }
558
+ });
559
+ }); })];
560
+ case 3:
561
+ log = _a.sent();
562
+ expect(log.text).toContain('Error sending invite');
563
+ expect(log.text).toContain('Error re-sending invite');
564
+ expect(log.text).toContain('in table Invite already exists');
565
+ expect(log.addresses).toEqual([]);
566
+ expect(log.text).toContain(digests.address('invitee-named@example.invalid'));
567
+ expect(log.text).toContain(digests.address('invitee-standing@example.invalid'));
568
+ return [2 /*return*/];
569
+ }
570
+ });
571
+ }); });
572
+ it('sign-in and sessions: a deactivated account refused, a session on a deactivated account, a session on a missing account', function () { return __awaiter(void 0, void 0, void 0, function () {
573
+ var log;
574
+ return __generator(this, function (_a) {
575
+ switch (_a.label) {
576
+ case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().insert(user_1.tables.User, {
577
+ name: 'Deactivated',
578
+ email: 'logs-deactivated@test.local',
579
+ password: (0, sha256_1.default)('correct-password').toString(),
580
+ emailVerified: true,
581
+ roles: [],
582
+ status: 'deactivated',
583
+ })];
584
+ case 1:
585
+ _a.sent();
586
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
587
+ var _a, _b, _c;
588
+ return __generator(this, function (_d) {
589
+ switch (_d.label) {
590
+ case 0:
591
+ _a = expect;
592
+ return [4 /*yield*/, (0, authenticate_1.authenticate)('Logs-Deactivated@test.local', 'correct-password')];
593
+ case 1:
594
+ _a.apply(void 0, [_d.sent()]).toBe('This account has been deactivated');
595
+ _b = expect;
596
+ return [4 /*yield*/, userCache_1.userCache.create('logs-session-1', 'logs-deactivated@test.local')];
597
+ case 2:
598
+ _b.apply(void 0, [(_d.sent()).id]).toBe('guest');
599
+ _c = expect;
600
+ return [4 /*yield*/, userCache_1.userCache.create('logs-session-2', 'logs-missing@test.local')];
601
+ case 3:
602
+ _c.apply(void 0, [(_d.sent()).id]).toBe('guest');
603
+ return [2 /*return*/];
604
+ }
605
+ });
606
+ }); })];
607
+ case 2:
608
+ log = _a.sent();
609
+ expect(log.addresses).toEqual([]);
610
+ expect(log.text).toContain(digests.account('logs-deactivated@test.local'));
611
+ expect(log.text).toContain(digests.account('logs-missing@test.local'));
612
+ return [2 /*return*/];
613
+ }
614
+ });
615
+ }); });
616
+ it('the sessions a deactivation kills, and a minted machine credential', function () { return __awaiter(void 0, void 0, void 0, function () {
617
+ var human, expires, declarations, log, _a;
618
+ return __generator(this, function (_b) {
619
+ switch (_b.label) {
620
+ case 0: return [4 /*yield*/, testEnv.createUser({ name: 'Soon Deactivated', email: 'logs-kill@test.local' })];
621
+ case 1:
622
+ human = _b.sent();
623
+ expires = new Date(Date.now() + 60 * 60 * 1000);
624
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().insert(user_1.tables.Session, {
625
+ sessionId: 'logs-kill',
626
+ session: '{}',
627
+ expires: expires,
628
+ userEmail: human.email,
629
+ })];
630
+ case 2:
631
+ _b.sent();
632
+ declarations = [new LogsMachineAccount()];
633
+ objectCache()['@proteinjs/db/SourceRecordLoader'] = declarations;
634
+ namedObjectCache()['@proteinjs/db/SourceRecordLoader'] = declarations.map(function (object) { return ({
635
+ qualifiedName: '@proteinjs/user-server-test/LogsMachineAccount',
636
+ packageName: '@proteinjs/user-server',
637
+ object: object,
638
+ }); });
639
+ return [4 /*yield*/, new db_1.SourceRecordSyncRunner().load()];
640
+ case 3:
641
+ _b.sent();
642
+ return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
643
+ var _a;
644
+ return __generator(this, function (_b) {
645
+ switch (_b.label) {
646
+ case 0: return [4 /*yield*/, (0, db_1.getDbAsSystem)().update(user_1.tables.User, { id: human.id, status: 'deactivated' })];
647
+ case 1:
648
+ _b.sent();
649
+ _a = expect;
650
+ return [4 /*yield*/, new MachineCredentials_1.MachineCredentials().mintCredential('machine-logs@test.local')];
651
+ case 2:
652
+ _a.apply(void 0, [(_b.sent()).secretName]).toBe('logs-ops-secret');
653
+ return [2 /*return*/];
654
+ }
655
+ });
656
+ }); })];
657
+ case 4:
658
+ log = _b.sent();
659
+ _a = expect;
660
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.Session, { sessionId: 'logs-kill' })];
661
+ case 5:
662
+ _a.apply(void 0, [_b.sent()]).toEqual([]);
663
+ expect(log.addresses).toEqual([]);
664
+ expect(log.text).toContain(digests.account('logs-kill@test.local'));
665
+ expect(log.text).toContain(digests.account('machine-logs@test.local'));
666
+ return [2 /*return*/];
667
+ }
668
+ });
669
+ }); });
670
+ it('the dev doors: a missing test account created, the first-admin grant, a session established', function () { return __awaiter(void 0, void 0, void 0, function () {
671
+ var admins, _i, _a, admin, log;
672
+ return __generator(this, function (_b) {
673
+ switch (_b.label) {
674
+ case 0:
675
+ process.env.DEVELOPMENT = 'true';
676
+ process.env.DEV_AUTO_LOGIN_EMAIL = 'logs-dev@test.local';
677
+ process.env.DEV_BOOTSTRAP_ADMIN_EMAIL = 'logs-dev@test.local';
678
+ delete process.env.DEV_BOOTSTRAP_ROLES;
679
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().query(user_1.tables.User, {})];
680
+ case 1:
681
+ admins = _b.sent();
682
+ _i = 0, _a = admins.filter(function (user) { var _a; return ((_a = user.roles) !== null && _a !== void 0 ? _a : []).includes('admin'); });
683
+ _b.label = 2;
684
+ case 2:
685
+ if (!(_i < _a.length)) return [3 /*break*/, 5];
686
+ admin = _a[_i];
687
+ return [4 /*yield*/, (0, db_1.getDbAsSystem)().update(user_1.tables.User, { id: admin.id, roles: [] })];
688
+ case 3:
689
+ _b.sent();
690
+ _b.label = 4;
691
+ case 4:
692
+ _i++;
693
+ return [3 /*break*/, 2];
694
+ case 5: return [4 /*yield*/, LogCapture_1.LogCapture.during(function () { return __awaiter(void 0, void 0, void 0, function () {
695
+ var _a;
696
+ return __generator(this, function (_b) {
697
+ switch (_b.label) {
698
+ case 0:
699
+ _a = expect;
700
+ return [4 /*yield*/, (0, devLoginHarness_1.invokeDevLogin)()];
701
+ case 1:
702
+ _a.apply(void 0, [(_b.sent()).loggedInAs]).toBe('logs-dev@test.local');
703
+ return [2 /*return*/];
704
+ }
705
+ });
706
+ }); })];
707
+ case 6:
708
+ log = _b.sent();
709
+ expect(log.text).toContain('Dev bootstrap admin door: granted');
710
+ expect(log.addresses).toEqual([]);
711
+ expect(log.text).toContain(digests.account('logs-dev@test.local'));
712
+ return [2 /*return*/];
713
+ }
714
+ });
715
+ }); });
716
+ });
717
+ //# sourceMappingURL=NoAddressesInLogs.integration.test.js.map