ti2 1.0.34 → 1.0.35
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.
- package/api.yml +70 -0
- package/controllers/__tests__/app.js +13 -1
- package/controllers/__tests__/user.js +10 -0
- package/controllers/app.js +61 -2
- package/index.js +1 -1
- package/package.json +1 -1
- package/test/plugin.js +19 -0
package/api.yml
CHANGED
|
@@ -96,6 +96,14 @@ components:
|
|
|
96
96
|
type: string
|
|
97
97
|
description: An identifier for this token, it should NOT be the token itself but a short safe version of it
|
|
98
98
|
example: 'expediaTestLocation-23'
|
|
99
|
+
UserAppValidate:
|
|
100
|
+
description: A user + App configuration to validate
|
|
101
|
+
type: object
|
|
102
|
+
properties:
|
|
103
|
+
tokenHint:
|
|
104
|
+
type: string
|
|
105
|
+
description: An identifier for this token, it should NOT be the token itself but a short safe version of it
|
|
106
|
+
example: 'expediaTestLocation-23'
|
|
99
107
|
UserId:
|
|
100
108
|
description: userId to create the token for
|
|
101
109
|
type: object
|
|
@@ -372,6 +380,30 @@ paths:
|
|
|
372
380
|
application/json:
|
|
373
381
|
schema:
|
|
374
382
|
$ref: '#/components/schemas/Message'
|
|
383
|
+
/app/{appKey}/tokenTemplate:
|
|
384
|
+
get:
|
|
385
|
+
security:
|
|
386
|
+
- bearerAuth: ['admin', 'user']
|
|
387
|
+
tags:
|
|
388
|
+
- admin
|
|
389
|
+
- user
|
|
390
|
+
summary: Should return the token template
|
|
391
|
+
operationId: tokenTemplate
|
|
392
|
+
parameters:
|
|
393
|
+
- name: appKey
|
|
394
|
+
in: path
|
|
395
|
+
description: Ann name to get the token template attributes
|
|
396
|
+
required: true
|
|
397
|
+
example: 'expedia'
|
|
398
|
+
schema:
|
|
399
|
+
type: string
|
|
400
|
+
responses:
|
|
401
|
+
200:
|
|
402
|
+
description: success
|
|
403
|
+
content:
|
|
404
|
+
application/json:
|
|
405
|
+
schema:
|
|
406
|
+
$ref: '#/components/schemas/AnyValue'
|
|
375
407
|
/apps:
|
|
376
408
|
get:
|
|
377
409
|
security:
|
|
@@ -563,6 +595,44 @@ paths:
|
|
|
563
595
|
application/json:
|
|
564
596
|
schema: '#/components/schemas/Message'
|
|
565
597
|
|
|
598
|
+
/{app}/{userId}/validate:
|
|
599
|
+
post:
|
|
600
|
+
tags:
|
|
601
|
+
- app
|
|
602
|
+
- user
|
|
603
|
+
summary: Should delete a user+appToken configuration, the auth token can be from an app or a user
|
|
604
|
+
security:
|
|
605
|
+
- bearerAuth: ['admin', 'app', 'user']
|
|
606
|
+
operationId: validateAppToken
|
|
607
|
+
parameters:
|
|
608
|
+
- name: app
|
|
609
|
+
in: path
|
|
610
|
+
description: app name to validate (must match app token)
|
|
611
|
+
example: 'expedia'
|
|
612
|
+
required: true
|
|
613
|
+
schema:
|
|
614
|
+
type: string
|
|
615
|
+
- name: userId
|
|
616
|
+
in: path
|
|
617
|
+
description: owner of the token to validate
|
|
618
|
+
example: '536830b6ed19afa44a000002'
|
|
619
|
+
required: true
|
|
620
|
+
schema:
|
|
621
|
+
type: string
|
|
622
|
+
requestBody:
|
|
623
|
+
description: The User+App configuration to validate
|
|
624
|
+
required: true
|
|
625
|
+
content:
|
|
626
|
+
application/json:
|
|
627
|
+
schema:
|
|
628
|
+
$ref: '#/components/schemas/UserAppValidate'
|
|
629
|
+
responses:
|
|
630
|
+
200:
|
|
631
|
+
description: success
|
|
632
|
+
content:
|
|
633
|
+
application/json:
|
|
634
|
+
schema: '#/components/schemas/Message'
|
|
635
|
+
|
|
566
636
|
/{app}/{userId}/jobRun:
|
|
567
637
|
security:
|
|
568
638
|
- bearerAuth: ['app', 'user', 'admin']
|
|
@@ -5,7 +5,7 @@ const R = require('ramda');
|
|
|
5
5
|
|
|
6
6
|
const testUtils = require('../../test/utils');
|
|
7
7
|
const slugify = require('../../test/slugify');
|
|
8
|
-
|
|
8
|
+
let appController = require('../app');
|
|
9
9
|
|
|
10
10
|
const { env: { adminKey, jwtSecret } } = process;
|
|
11
11
|
|
|
@@ -37,6 +37,7 @@ describe('app', () => {
|
|
|
37
37
|
({ doApiPost, doApiGet, doApiPut } = await testUtils({
|
|
38
38
|
plugins: [appName],
|
|
39
39
|
}));
|
|
40
|
+
appController = appController([appName]);
|
|
40
41
|
});
|
|
41
42
|
it('should create a new app', async () => {
|
|
42
43
|
({ value: appKey } = await doApiPost({
|
|
@@ -69,6 +70,17 @@ describe('app', () => {
|
|
|
69
70
|
});
|
|
70
71
|
expect(parseInt(value, 10)).toBeGreaterThan(0);
|
|
71
72
|
});
|
|
73
|
+
it('should be able to test a user token for the app', async () => {
|
|
74
|
+
const { valid } = await doApiPost({
|
|
75
|
+
url: `/${appName}/${userId}/validate`,
|
|
76
|
+
token: appKey,
|
|
77
|
+
payload: {
|
|
78
|
+
tokenHint: apiKey.split('-')[0],
|
|
79
|
+
token,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
expect(valid).toBe(true);
|
|
83
|
+
});
|
|
72
84
|
describe('jobs', () => {
|
|
73
85
|
it('the scheduled job should have been created for the user', async () => {
|
|
74
86
|
const jobs = R.path(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* globals describe it expect beforeAll, jest, afterEach */
|
|
2
2
|
|
|
3
|
+
const R = require('ramda');
|
|
3
4
|
const chance = require('chance').Chance();
|
|
4
5
|
const testUtils = require('../../test/utils');
|
|
5
6
|
const slugify = require('../../test/slugify');
|
|
@@ -76,6 +77,15 @@ describe('user', () => {
|
|
|
76
77
|
]),
|
|
77
78
|
);
|
|
78
79
|
});
|
|
80
|
+
it('should be able to get the app\'s token template', async () => {
|
|
81
|
+
const { template } = await doApiGet({
|
|
82
|
+
url: `/app/${appName}/tokenTemplate`,
|
|
83
|
+
token: userKey,
|
|
84
|
+
payload: {},
|
|
85
|
+
});
|
|
86
|
+
expect(template).toBeTruthy();
|
|
87
|
+
expect(R.path(['apiKey', 'regExp', 'source'], template)).toBeTruthy();
|
|
88
|
+
});
|
|
79
89
|
it('should be able to delete a user/app key', async () => {
|
|
80
90
|
await doApiDelete({
|
|
81
91
|
url: `/${appName}/${userId}`,
|
package/controllers/app.js
CHANGED
|
@@ -26,6 +26,30 @@ const jwtEncode = async (req, res, next) => {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
const tokenTemplate = async (req, res, next) => {
|
|
30
|
+
const {
|
|
31
|
+
params: {
|
|
32
|
+
appKey: pluginName,
|
|
33
|
+
},
|
|
34
|
+
} = req;
|
|
35
|
+
try {
|
|
36
|
+
const thePlugin = req.app.plugins.find(({ name }) => name === pluginName);
|
|
37
|
+
assert(thePlugin);
|
|
38
|
+
let template = thePlugin.tokenTemplate();
|
|
39
|
+
const safeRegExp = el => ({
|
|
40
|
+
...el,
|
|
41
|
+
regExp: {
|
|
42
|
+
flags: el.regExp.flags,
|
|
43
|
+
source: el.regExp.source,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
template = R.map(safeRegExp, template);
|
|
47
|
+
return res.json({ template });
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return next(err);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
29
53
|
const createAppToken = async (req, res, next) => {
|
|
30
54
|
const {
|
|
31
55
|
body: {
|
|
@@ -141,6 +165,39 @@ const deleteAppToken = async (req, res, next) => {
|
|
|
141
165
|
return res.json({ message: `${retVal} erased` });
|
|
142
166
|
};
|
|
143
167
|
|
|
168
|
+
const validateAppToken = plugins => async (req, res, next) => {
|
|
169
|
+
const {
|
|
170
|
+
body: {
|
|
171
|
+
tokenHint: hint,
|
|
172
|
+
},
|
|
173
|
+
params: {
|
|
174
|
+
app: appKey,
|
|
175
|
+
userId,
|
|
176
|
+
},
|
|
177
|
+
} = req;
|
|
178
|
+
try {
|
|
179
|
+
const app = plugins.find(({ name }) => name === appKey);
|
|
180
|
+
assert(app, `could not find the app ${appKey}`);
|
|
181
|
+
const userAppKeys = await sqldb.UserAppKey.findOne({
|
|
182
|
+
where: {
|
|
183
|
+
userId,
|
|
184
|
+
integrationId: appKey,
|
|
185
|
+
...(hint ? { hint } : {}),
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
assert(userAppKeys, 'could not find the app key');
|
|
189
|
+
const token = userAppKeys.appKey;
|
|
190
|
+
assert(app.validateToken, `could not find the validateToken method for ${appKey}`);
|
|
191
|
+
const valid = await app.validateToken({
|
|
192
|
+
token,
|
|
193
|
+
});
|
|
194
|
+
return res.json({ valid });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
return next(err);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
|
|
144
201
|
const listAppTokens = async (req, res, next) => {
|
|
145
202
|
const { params: { app: integrationId } } = req;
|
|
146
203
|
try {
|
|
@@ -253,7 +310,7 @@ const getJobStatus = async (req, res, next) => {
|
|
|
253
310
|
}
|
|
254
311
|
};
|
|
255
312
|
|
|
256
|
-
module.exports = {
|
|
313
|
+
module.exports = plugins => ({
|
|
257
314
|
createAppToken,
|
|
258
315
|
deleteAppToken,
|
|
259
316
|
getAppScheduledJobs,
|
|
@@ -262,4 +319,6 @@ module.exports = {
|
|
|
262
319
|
listAppTokens,
|
|
263
320
|
migrateApp,
|
|
264
321
|
runAppJob,
|
|
265
|
-
|
|
322
|
+
tokenTemplate,
|
|
323
|
+
validateAppToken: validateAppToken(plugins),
|
|
324
|
+
});
|
package/index.js
CHANGED
package/package.json
CHANGED
package/test/plugin.js
CHANGED
|
@@ -54,6 +54,13 @@ class Plugin {
|
|
|
54
54
|
this.searchQuote = jestPlugin.fn(() => ({ quote: [{ id: chance.guid() }] }));
|
|
55
55
|
this.createBooking = jestPlugin.fn(() => {});
|
|
56
56
|
this.queryAllotment = jestPlugin.fn(() => ({ allotments: [] }));
|
|
57
|
+
this.tokenTemplate = jestPlugin.fn(() => ({
|
|
58
|
+
apiKey: {
|
|
59
|
+
type: 'text',
|
|
60
|
+
regExp: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/,
|
|
61
|
+
description: 'Api Key',
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
66
|
* Background and schedule Jos
|
|
@@ -81,6 +88,18 @@ class Plugin {
|
|
|
81
88
|
validateToken() {
|
|
82
89
|
return Boolean(this);
|
|
83
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns the the token template of a plugin.
|
|
94
|
+
* Properties are dynamic since every attribute should correspond to a token
|
|
95
|
+
* key, every attribute should contain at least the type (ie text) a regExp and
|
|
96
|
+
* a description.
|
|
97
|
+
* The regExp property gets decomposed into both source and flags as strings.
|
|
98
|
+
*/
|
|
99
|
+
tokenTemplate() {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
/**
|
|
85
104
|
* GPS Location Object
|
|
86
105
|
* @typedef {Object} GPSLocation
|