@boxyhq/saml-jackson 0.2.4 → 0.3.0-beta.246

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 (100) hide show
  1. package/Dockerfile +9 -7
  2. package/README.md +1 -2
  3. package/dist/controller/api.d.ts +32 -0
  4. package/dist/controller/api.js +193 -0
  5. package/dist/controller/error.d.ts +5 -0
  6. package/dist/controller/error.js +12 -0
  7. package/dist/controller/oauth/allowed.d.ts +1 -0
  8. package/dist/controller/oauth/allowed.js +17 -0
  9. package/dist/controller/oauth/code-verifier.d.ts +2 -0
  10. package/dist/controller/oauth/code-verifier.js +15 -0
  11. package/dist/controller/oauth/redirect.d.ts +1 -0
  12. package/dist/controller/oauth/redirect.js +11 -0
  13. package/dist/controller/oauth.d.ts +23 -0
  14. package/dist/controller/oauth.js +263 -0
  15. package/dist/controller/utils.d.ts +6 -0
  16. package/dist/controller/utils.js +17 -0
  17. package/dist/db/db.d.ts +15 -0
  18. package/dist/db/db.js +107 -0
  19. package/dist/db/encrypter.d.ts +3 -0
  20. package/dist/db/encrypter.js +29 -0
  21. package/dist/db/mem.d.ts +20 -0
  22. package/dist/db/mem.js +128 -0
  23. package/dist/db/mongo.d.ts +17 -0
  24. package/dist/db/mongo.js +106 -0
  25. package/dist/db/redis.d.ts +15 -0
  26. package/dist/db/redis.js +107 -0
  27. package/dist/db/sql/entity/JacksonIndex.d.ts +7 -0
  28. package/dist/db/sql/entity/JacksonIndex.js +41 -0
  29. package/dist/db/sql/entity/JacksonStore.d.ts +6 -0
  30. package/dist/db/sql/entity/JacksonStore.js +42 -0
  31. package/dist/db/sql/entity/JacksonTTL.d.ts +4 -0
  32. package/dist/db/sql/entity/JacksonTTL.js +29 -0
  33. package/dist/db/sql/sql.d.ts +20 -0
  34. package/dist/db/sql/sql.js +174 -0
  35. package/dist/db/store.d.ts +5 -0
  36. package/dist/db/store.js +68 -0
  37. package/dist/db/utils.d.ts +7 -0
  38. package/dist/db/utils.js +29 -0
  39. package/dist/env.d.ts +22 -0
  40. package/dist/env.js +35 -0
  41. package/dist/index.d.ts +9 -0
  42. package/dist/index.js +80 -0
  43. package/dist/jackson.d.ts +1 -0
  44. package/dist/jackson.js +153 -0
  45. package/dist/read-config.d.ts +3 -0
  46. package/dist/read-config.js +50 -0
  47. package/dist/saml/claims.d.ts +6 -0
  48. package/dist/saml/claims.js +35 -0
  49. package/dist/saml/saml.d.ts +11 -0
  50. package/dist/saml/saml.js +200 -0
  51. package/dist/saml/x509.d.ts +7 -0
  52. package/dist/saml/x509.js +69 -0
  53. package/dist/typings.d.ts +137 -0
  54. package/dist/typings.js +2 -0
  55. package/package.json +41 -21
  56. package/.dockerignore +0 -2
  57. package/.eslintrc.js +0 -13
  58. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
  59. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  60. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -43
  61. package/.github/pull_request_template.md +0 -31
  62. package/.github/workflows/codesee-arch-diagram.yml +0 -81
  63. package/.github/workflows/main.yml +0 -123
  64. package/_dev/docker-compose.yml +0 -37
  65. package/map.js +0 -1
  66. package/prettier.config.js +0 -4
  67. package/src/controller/api.js +0 -167
  68. package/src/controller/error.js +0 -12
  69. package/src/controller/oauth/allowed.js +0 -19
  70. package/src/controller/oauth/code-verifier.js +0 -16
  71. package/src/controller/oauth/redirect.js +0 -18
  72. package/src/controller/oauth.js +0 -321
  73. package/src/controller/utils.js +0 -19
  74. package/src/db/db.js +0 -81
  75. package/src/db/db.test.js +0 -302
  76. package/src/db/encrypter.js +0 -36
  77. package/src/db/mem.js +0 -111
  78. package/src/db/mongo.js +0 -89
  79. package/src/db/redis.js +0 -88
  80. package/src/db/sql/entity/JacksonIndex.js +0 -42
  81. package/src/db/sql/entity/JacksonStore.js +0 -42
  82. package/src/db/sql/entity/JacksonTTL.js +0 -23
  83. package/src/db/sql/model/JacksonIndex.js +0 -9
  84. package/src/db/sql/model/JacksonStore.js +0 -10
  85. package/src/db/sql/model/JacksonTTL.js +0 -8
  86. package/src/db/sql/sql.js +0 -153
  87. package/src/db/store.js +0 -42
  88. package/src/db/utils.js +0 -30
  89. package/src/env.js +0 -39
  90. package/src/index.js +0 -67
  91. package/src/jackson.js +0 -161
  92. package/src/read-config.js +0 -24
  93. package/src/saml/claims.js +0 -40
  94. package/src/saml/saml.js +0 -223
  95. package/src/saml/x509.js +0 -48
  96. package/src/test/api.test.js +0 -186
  97. package/src/test/data/metadata/boxyhq.js +0 -6
  98. package/src/test/data/metadata/boxyhq.xml +0 -30
  99. package/src/test/data/saml_response +0 -1
  100. package/src/test/oauth.test.js +0 -342
@@ -1,321 +0,0 @@
1
- const crypto = require('crypto');
2
-
3
- const saml = require('../saml/saml.js');
4
- const codeVerifier = require('./oauth/code-verifier.js');
5
- const { indexNames } = require('./utils.js');
6
- const dbutils = require('../db/utils.js');
7
- const redirect = require('./oauth/redirect.js');
8
- const allowed = require('./oauth/allowed.js');
9
- const { JacksonError } = require('./error.js');
10
-
11
- let configStore;
12
- let sessionStore;
13
- let codeStore;
14
- let tokenStore;
15
- let options;
16
-
17
- const relayStatePrefix = 'boxyhq_jackson_';
18
-
19
- function getEncodedClientId(client_id) {
20
- try {
21
- const sp = new URLSearchParams(client_id);
22
- const tenant = sp.get('tenant');
23
- const product = sp.get('product');
24
- if (tenant && product) {
25
- return {
26
- tenant: sp.get('tenant'),
27
- product: sp.get('product'),
28
- };
29
- }
30
-
31
- return null;
32
- } catch (err) {
33
- return null;
34
- }
35
- }
36
-
37
- const authorize = async (body) => {
38
- const {
39
- response_type = 'code',
40
- client_id,
41
- redirect_uri,
42
- state,
43
- tenant,
44
- product,
45
- code_challenge,
46
- code_challenge_method = '',
47
- // eslint-disable-next-line no-unused-vars
48
- provider = 'saml',
49
- } = body;
50
-
51
- if (!redirect_uri) {
52
- throw new JacksonError('Please specify a redirect URL.', 400);
53
- }
54
-
55
- if (!state) {
56
- throw new JacksonError(
57
- 'Please specify a state to safeguard against XSRF attacks.',
58
- 400
59
- );
60
- }
61
-
62
- let samlConfig;
63
-
64
- if (tenant && product) {
65
- const samlConfigs = await configStore.getByIndex({
66
- name: indexNames.tenantProduct,
67
- value: dbutils.keyFromParts(tenant, product),
68
- });
69
-
70
- if (!samlConfigs || samlConfigs.length === 0) {
71
- throw new JacksonError('SAML configuration not found.', 403);
72
- }
73
-
74
- // TODO: Support multiple matches
75
- samlConfig = samlConfigs[0];
76
- } else if (
77
- client_id &&
78
- client_id !== '' &&
79
- client_id !== 'undefined' &&
80
- client_id !== 'null'
81
- ) {
82
- // if tenant and product are encoded in the client_id then we parse it and check for the relevant config(s)
83
- const sp = getEncodedClientId(client_id);
84
- if (sp) {
85
- const samlConfigs = await configStore.getByIndex({
86
- name: indexNames.tenantProduct,
87
- value: dbutils.keyFromParts(sp.tenant, sp.product),
88
- });
89
-
90
- if (!samlConfigs || samlConfigs.length === 0) {
91
- throw new JacksonError('SAML configuration not found.', 403);
92
- }
93
-
94
- // TODO: Support multiple matches
95
- samlConfig = samlConfigs[0];
96
- } else {
97
- samlConfig = await configStore.get(client_id);
98
- }
99
- } else {
100
- throw new JacksonError(
101
- 'You need to specify client_id or tenant & product',
102
- 403
103
- );
104
- }
105
-
106
- if (!samlConfig) {
107
- throw new JacksonError('SAML configuration not found.', 403);
108
- }
109
-
110
- if (!allowed.redirect(redirect_uri, samlConfig.redirectUrl)) {
111
- throw new JacksonError('Redirect URL is not allowed.', 403);
112
- }
113
-
114
- const samlReq = saml.request({
115
- entityID: options.samlAudience,
116
- callbackUrl: options.externalUrl + options.samlPath,
117
- signingKey: samlConfig.certs.privateKey,
118
- });
119
-
120
- const sessionId = crypto.randomBytes(16).toString('hex');
121
-
122
- await sessionStore.put(sessionId, {
123
- id: samlReq.id,
124
- redirect_uri,
125
- response_type,
126
- state,
127
- code_challenge,
128
- code_challenge_method,
129
- });
130
-
131
- const redirectUrl = redirect.success(samlConfig.idpMetadata.sso.redirectUrl, {
132
- RelayState: relayStatePrefix + sessionId,
133
- SAMLRequest: Buffer.from(samlReq.request).toString('base64'),
134
- });
135
-
136
- return { redirect_url: redirectUrl };
137
- };
138
-
139
- const samlResponse = async (body) => {
140
- const { SAMLResponse } = body; // RelayState will contain the sessionId from earlier quasi-oauth flow
141
-
142
- let RelayState = body.RelayState || '';
143
-
144
- if (!options.idpEnabled && !RelayState.startsWith(relayStatePrefix)) {
145
- // IDP is disabled so block the request
146
-
147
- throw new JacksonError(
148
- 'IdP (Identity Provider) flow has been disabled. Please head to your Service Provider to login.',
149
- 403
150
- );
151
- }
152
-
153
- if (!RelayState.startsWith(relayStatePrefix)) {
154
- RelayState = '';
155
- }
156
-
157
- RelayState = RelayState.replace(relayStatePrefix, '');
158
-
159
- const rawResponse = Buffer.from(SAMLResponse, 'base64').toString();
160
-
161
- const parsedResp = await saml.parseAsync(rawResponse);
162
-
163
- const samlConfigs = await configStore.getByIndex({
164
- name: indexNames.entityID,
165
- value: parsedResp.issuer,
166
- });
167
-
168
- if (!samlConfigs || samlConfigs.length === 0) {
169
- throw new JacksonError('SAML configuration not found.', 403);
170
- }
171
-
172
- // TODO: Support multiple matches
173
- const samlConfig = samlConfigs[0];
174
-
175
- let session;
176
-
177
- if (RelayState !== '') {
178
- session = await sessionStore.get(RelayState);
179
- if (!session) {
180
- throw new JacksonError(
181
- 'Unable to validate state from the origin request.',
182
- 403
183
- );
184
- }
185
- }
186
-
187
- let validateOpts = {
188
- thumbprint: samlConfig.idpMetadata.thumbprint,
189
- audience: options.samlAudience,
190
- };
191
-
192
- if (session && session.id) {
193
- validateOpts.inResponseTo = session.id;
194
- }
195
-
196
- const profile = await saml.validateAsync(rawResponse, validateOpts);
197
-
198
- // store details against a code
199
- const code = crypto.randomBytes(20).toString('hex');
200
-
201
- let codeVal = {
202
- profile,
203
- clientID: samlConfig.clientID,
204
- clientSecret: samlConfig.clientSecret,
205
- };
206
-
207
- if (session) {
208
- codeVal.session = session;
209
- }
210
-
211
- await codeStore.put(code, codeVal);
212
-
213
- if (
214
- session &&
215
- session.redirect_uri &&
216
- !allowed.redirect(session.redirect_uri, samlConfig.redirectUrl)
217
- ) {
218
- throw new JacksonError('Redirect URL is not allowed.', 403);
219
- }
220
-
221
- let params = {
222
- code,
223
- };
224
-
225
- if (session && session.state) {
226
- params.state = session.state;
227
- }
228
-
229
- const redirectUrl = redirect.success(
230
- (session && session.redirect_uri) || samlConfig.defaultRedirectUrl,
231
- params
232
- );
233
-
234
- return { redirect_url: redirectUrl };
235
- };
236
-
237
- const token = async (body) => {
238
- const {
239
- client_id,
240
- client_secret,
241
- code_verifier,
242
- code,
243
- grant_type = 'authorization_code',
244
- } = body;
245
-
246
- if (grant_type !== 'authorization_code') {
247
- throw new JacksonError('Unsupported grant_type', 400);
248
- }
249
-
250
- if (!code) {
251
- throw new JacksonError('Please specify code', 400);
252
- }
253
-
254
- const codeVal = await codeStore.get(code);
255
- if (!codeVal || !codeVal.profile) {
256
- throw new JacksonError('Invalid code', 403);
257
- }
258
-
259
- if (client_id && client_secret) {
260
- // check if we have an encoded client_id
261
- if (client_id !== 'dummy' && client_secret !== 'dummy') {
262
- const sp = getEncodedClientId(client_id);
263
- if (!sp) {
264
- // OAuth flow
265
- if (
266
- client_id !== codeVal.clientID ||
267
- client_secret !== codeVal.clientSecret
268
- ) {
269
- throw new JacksonError('Invalid client_id or client_secret', 401);
270
- }
271
- }
272
- }
273
- } else if (code_verifier) {
274
- // PKCE flow
275
- let cv = code_verifier;
276
- if (codeVal.session.code_challenge_method.toLowerCase() === 's256') {
277
- cv = codeVerifier.encode(code_verifier);
278
- }
279
-
280
- if (codeVal.session.code_challenge !== cv) {
281
- throw new JacksonError('Invalid code_verifier', 401);
282
- }
283
- } else if (codeVal && codeVal.session) {
284
- throw new JacksonError(
285
- 'Please specify client_secret or code_verifier',
286
- 401
287
- );
288
- }
289
-
290
- // store details against a token
291
- const token = crypto.randomBytes(20).toString('hex');
292
-
293
- await tokenStore.put(token, codeVal.profile);
294
-
295
- return {
296
- access_token: token,
297
- token_type: 'bearer',
298
- expires_in: options.db.ttl,
299
- };
300
- };
301
-
302
- const userInfo = async (token) => {
303
- const { claims } = await tokenStore.get(token);
304
-
305
- return claims;
306
- };
307
-
308
- module.exports = (opts) => {
309
- configStore = opts.configStore;
310
- sessionStore = opts.sessionStore;
311
- codeStore = opts.codeStore;
312
- tokenStore = opts.tokenStore;
313
- options = opts.opts;
314
-
315
- return {
316
- authorize,
317
- samlResponse,
318
- token,
319
- userInfo,
320
- };
321
- };
@@ -1,19 +0,0 @@
1
- const indexNames = {
2
- entityID: 'entityID',
3
- tenantProduct: 'tenantProduct',
4
- };
5
-
6
- const extractAuthToken = (req) => {
7
- const authHeader = req.get('authorization');
8
- const parts = (authHeader || '').split(' ');
9
- if (parts.length > 1) {
10
- return parts[1];
11
- }
12
-
13
- return null;
14
- };
15
-
16
- module.exports = {
17
- indexNames,
18
- extractAuthToken,
19
- };
package/src/db/db.js DELETED
@@ -1,81 +0,0 @@
1
- const mem = require('./mem.js');
2
- const mongo = require('./mongo.js');
3
- const redis = require('./redis.js');
4
- const sql = require('./sql/sql.js');
5
- const store = require('./store.js');
6
- const encrypter = require('./encrypter.js');
7
-
8
- const decrypt = (res, encryptionKey) => {
9
- if (res.iv && res.tag) {
10
- return JSON.parse(
11
- encrypter.decrypt(res.value, res.iv, res.tag, encryptionKey)
12
- );
13
- }
14
-
15
- return JSON.parse(res.value);
16
- };
17
-
18
- class DB {
19
- constructor(db, encryptionKey) {
20
- this.db = db;
21
- this.encryptionKey = encryptionKey;
22
- }
23
-
24
- async get(namespace, key) {
25
- const res = await this.db.get(namespace, key);
26
- if (!res) {
27
- return null;
28
- }
29
-
30
- return decrypt(res, this.encryptionKey);
31
- }
32
-
33
- async getByIndex(namespace, idx) {
34
- const res = await this.db.getByIndex(namespace, idx);
35
- const encryptionKey = this.encryptionKey;
36
- return res.map((r) => {
37
- return decrypt(r, encryptionKey);
38
- });
39
- }
40
-
41
- // ttl is in seconds
42
- async put(namespace, key, val, ttl = 0, ...indexes) {
43
- if (ttl > 0 && indexes && indexes.length > 0) {
44
- throw new Error('secondary indexes not allow on a store with ttl');
45
- }
46
-
47
- const dbVal = this.encryptionKey
48
- ? encrypter.encrypt(JSON.stringify(val), this.encryptionKey)
49
- : { value: JSON.stringify(val) };
50
-
51
- return await this.db.put(namespace, key, dbVal, ttl, ...indexes);
52
- }
53
-
54
- async delete(namespace, key) {
55
- return await this.db.delete(namespace, key);
56
- }
57
-
58
- store(namespace, ttl = 0) {
59
- return store.new(namespace, this, ttl);
60
- }
61
- }
62
-
63
- module.exports = {
64
- new: async (options) => {
65
- const encryptionKey = options.encryptionKey
66
- ? Buffer.from(options.encryptionKey, 'latin1')
67
- : null;
68
- switch (options.engine) {
69
- case 'redis':
70
- return new DB(await redis.new(options), encryptionKey);
71
- case 'sql':
72
- return new DB(await sql.new(options), encryptionKey);
73
- case 'mongo':
74
- return new DB(await mongo.new(options), encryptionKey);
75
- case 'mem':
76
- return new DB(await mem.new(options), encryptionKey);
77
- default:
78
- throw new Error('unsupported db engine: ' + options.engine);
79
- }
80
- },
81
- };
package/src/db/db.test.js DELETED
@@ -1,302 +0,0 @@
1
- const t = require('tap');
2
-
3
- const DB = require('./db.js');
4
-
5
- const encryptionKey = '3yGrTcnKPBqqHoH3zZMAU6nt4bmIYb2q';
6
-
7
- let configStores = [];
8
- let ttlStores = [];
9
- const ttl = 3;
10
-
11
- const record1 = {
12
- id: '1',
13
- name: 'Deepak',
14
- city: 'London',
15
- };
16
- const record2 = {
17
- id: '2',
18
- name: 'Sama',
19
- city: 'London',
20
- };
21
-
22
- const memDbConfig = {
23
- engine: 'mem',
24
- ttl: 1,
25
- };
26
-
27
- const redisDbConfig = {
28
- engine: 'redis',
29
- url: 'redis://localhost:6379',
30
- };
31
-
32
- const postgresDbConfig = {
33
- engine: 'sql',
34
- url: 'postgresql://postgres:postgres@localhost:5432/postgres',
35
- type: 'postgres',
36
- ttl: 1,
37
- cleanupLimit: 1,
38
- };
39
-
40
- const mongoDbConfig = {
41
- engine: 'mongo',
42
- url: 'mongodb://localhost:27017/jackson',
43
- };
44
-
45
- const mysqlDbConfig = {
46
- engine: 'sql',
47
- url: 'mysql://root:mysql@localhost:3307/mysql',
48
- type: 'mysql',
49
- ttl: 1,
50
- cleanupLimit: 1,
51
- };
52
-
53
- const mariadbDbConfig = {
54
- engine: 'sql',
55
- url: 'mariadb://root@localhost:3306/mysql',
56
- type: 'mariadb',
57
- ttl: 1,
58
- cleanupLimit: 1,
59
- };
60
-
61
- const dbs = [
62
- {
63
- ...memDbConfig,
64
- },
65
- {
66
- ...memDbConfig,
67
- encryptionKey,
68
- },
69
- {
70
- ...redisDbConfig,
71
- },
72
- {
73
- ...redisDbConfig,
74
- encryptionKey,
75
- },
76
- {
77
- ...postgresDbConfig,
78
- },
79
- {
80
- ...postgresDbConfig,
81
- encryptionKey,
82
- },
83
- {
84
- ...mongoDbConfig,
85
- },
86
- {
87
- ...mongoDbConfig,
88
- encryptionKey,
89
- },
90
- {
91
- ...mysqlDbConfig,
92
- },
93
- {
94
- ...mysqlDbConfig,
95
- encryptionKey,
96
- },
97
- {
98
- ...mariadbDbConfig,
99
- },
100
- {
101
- ...mariadbDbConfig,
102
- encryptionKey,
103
- },
104
- ];
105
-
106
- t.before(async () => {
107
- for (const idx in dbs) {
108
- const opts = dbs[idx];
109
- const db = await DB.new(opts);
110
-
111
- configStores.push(db.store('saml:config'));
112
- ttlStores.push(db.store('oauth:session', ttl));
113
- }
114
- });
115
-
116
- t.teardown(async () => {
117
- process.exit(0);
118
- });
119
-
120
- t.test('dbs', ({ end }) => {
121
- for (const idx in configStores) {
122
- const configStore = configStores[idx];
123
- const ttlStore = ttlStores[idx];
124
- let dbEngine = dbs[idx].engine;
125
- if (dbs[idx].type) {
126
- dbEngine += ': ' + dbs[idx].type;
127
- }
128
- t.test('put(): ' + dbEngine, async (t) => {
129
- await configStore.put(
130
- record1.id,
131
- record1,
132
- {
133
- // secondary index on city
134
- name: 'city',
135
- value: record1.city,
136
- },
137
- {
138
- // secondary index on name
139
- name: 'name',
140
- value: record1.name,
141
- }
142
- );
143
-
144
- await configStore.put(
145
- record2.id,
146
- record2,
147
- {
148
- // secondary index on city
149
- name: 'city',
150
- value: record2.city,
151
- },
152
- {
153
- // secondary index on name
154
- name: 'name',
155
- value: record2.name,
156
- }
157
- );
158
-
159
- t.end();
160
- });
161
-
162
- t.test('get(): ' + dbEngine, async (t) => {
163
- const ret1 = await configStore.get(record1.id);
164
- const ret2 = await configStore.get(record2.id);
165
-
166
- t.same(ret1, record1, 'unable to get record1');
167
- t.same(ret2, record2, 'unable to get record2');
168
-
169
- t.end();
170
- });
171
-
172
- t.test('getByIndex(): ' + dbEngine, async (t) => {
173
- const ret1 = await configStore.getByIndex({
174
- name: 'name',
175
- value: record1.name,
176
- });
177
-
178
- const ret2 = await configStore.getByIndex({
179
- name: 'city',
180
- value: record1.city,
181
- });
182
-
183
- t.same(ret1, [record1], 'unable to get index "name"');
184
- t.same(
185
- ret2.sort((a, b) => a.id.localeCompare(b.id)),
186
- [record1, record2].sort((a, b) => a.id.localeCompare(b.id)),
187
- 'unable to get index "city"'
188
- );
189
-
190
- t.end();
191
- });
192
-
193
- t.test('delete(): ' + dbEngine, async (t) => {
194
- await configStore.delete(record1.id);
195
-
196
- const ret0 = await configStore.getByIndex({
197
- name: 'city',
198
- value: record1.city,
199
- });
200
-
201
- t.same(ret0, [record2], 'unable to get index "city" after delete');
202
-
203
- await configStore.delete(record2.id);
204
-
205
- const ret1 = await configStore.get(record1.id);
206
- const ret2 = await configStore.get(record2.id);
207
-
208
- const ret3 = await configStore.getByIndex({
209
- name: 'name',
210
- value: record1.name,
211
- });
212
- const ret4 = await configStore.getByIndex({
213
- name: 'city',
214
- value: record1.city,
215
- });
216
-
217
- t.same(ret1, null, 'delete for record1 failed');
218
- t.same(ret2, null, 'delete for record2 failed');
219
-
220
- t.same(ret3, [], 'delete for record1 failed');
221
- t.same(ret4, [], 'delete for record2 failed');
222
-
223
- t.end();
224
- });
225
-
226
- t.test('ttl indexes: ' + dbEngine, async (t) => {
227
- try {
228
- await ttlStore.put(
229
- record1.id,
230
- record1,
231
- {
232
- // secondary index on city
233
- name: 'city',
234
- value: record1.city,
235
- },
236
- {
237
- // secondary index on name
238
- name: 'name',
239
- value: record1.name,
240
- }
241
- );
242
-
243
- t.fail('expecting a secondary indexes not allow on a store with ttl');
244
- } catch (err) {
245
- t.ok(err, 'got expected error');
246
- }
247
-
248
- t.end();
249
- });
250
-
251
- t.test('ttl put(): ' + dbEngine, async (t) => {
252
- await ttlStore.put(record1.id, record1);
253
-
254
- await ttlStore.put(record2.id, record2);
255
-
256
- t.end();
257
- });
258
-
259
- t.test('ttl get(): ' + dbEngine, async (t) => {
260
- const ret1 = await ttlStore.get(record1.id);
261
- const ret2 = await ttlStore.get(record2.id);
262
-
263
- t.same(ret1, record1, 'unable to get record1');
264
- t.same(ret2, record2, 'unable to get record2');
265
-
266
- t.end();
267
- });
268
-
269
- t.test('ttl expiry: ' + dbEngine, async (t) => {
270
- // mongo runs ttl task every 60 seconds
271
- if (dbEngine.startsWith('mongo')) {
272
- t.end();
273
- return;
274
- }
275
-
276
- await new Promise((resolve) =>
277
- setTimeout(resolve, (2 * ttl + 0.5) * 1000)
278
- );
279
-
280
- const ret1 = await ttlStore.get(record1.id);
281
- const ret2 = await ttlStore.get(record2.id);
282
-
283
- t.same(ret1, null, 'ttl for record1 failed');
284
- t.same(ret2, null, 'ttl for record2 failed');
285
-
286
- t.end();
287
- });
288
- }
289
-
290
- t.test('db.new() error', async (t) => {
291
- try {
292
- await DB.new('somedb');
293
- t.fail('expecting an unsupported db error');
294
- } catch (err) {
295
- t.ok(err, 'got expected error');
296
- }
297
-
298
- t.end();
299
- });
300
-
301
- end();
302
- });