@boxyhq/saml-jackson 0.1.5-beta.97 → 0.1.6-beta.148
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/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +43 -0
- package/.github/pull_request_template.md +31 -0
- package/.github/workflows/main.yml +1 -1
- package/README.md +186 -71
- package/package.json +15 -6
- package/src/controller/api.js +47 -0
- package/src/controller/oauth/redirect.js +4 -3
- package/src/controller/oauth.js +48 -60
- package/src/controller/utils.js +11 -0
- package/src/db/db.test.js +1 -1
- package/src/db/sql/entity/JacksonStore.js +0 -4
- package/src/db/sql/entity/JacksonTTL.js +23 -0
- package/src/db/sql/model/JacksonStore.js +1 -2
- package/src/db/sql/model/JacksonTTL.js +8 -0
- package/src/db/sql/sql.js +20 -9
- package/src/env.js +3 -0
- package/src/error.js +12 -0
- package/src/index.js +2 -2
- package/src/jackson.js +63 -9
- package/src/saml/claims.js +40 -0
- package/src/saml/saml.js +29 -3
- package/src/test/data/metadata/boxyhq.js +6 -0
- package/src/test/data/metadata/boxyhq.xml +30 -0
- package/src/test/data/saml_response +1 -0
- package/src/test/oauth.test.js +343 -0
package/src/controller/oauth.js
CHANGED
@@ -6,6 +6,7 @@ const { indexNames } = require('./utils.js');
|
|
6
6
|
const dbutils = require('../db/utils.js');
|
7
7
|
const redirect = require('./oauth/redirect.js');
|
8
8
|
const allowed = require('./oauth/allowed.js');
|
9
|
+
const { JacksonError } = require('../error.js');
|
9
10
|
|
10
11
|
let configStore;
|
11
12
|
let sessionStore;
|
@@ -15,16 +16,6 @@ let options;
|
|
15
16
|
|
16
17
|
const relayStatePrefix = 'boxyhq_jackson_';
|
17
18
|
|
18
|
-
const extractBearerToken = (req) => {
|
19
|
-
const authHeader = req.get('authorization');
|
20
|
-
const parts = (authHeader || '').split(' ');
|
21
|
-
if (parts.length > 1) {
|
22
|
-
return parts[1];
|
23
|
-
}
|
24
|
-
|
25
|
-
return null;
|
26
|
-
};
|
27
|
-
|
28
19
|
function getEncodedClientId(client_id) {
|
29
20
|
try {
|
30
21
|
const sp = new URLSearchParams(client_id);
|
@@ -43,7 +34,7 @@ function getEncodedClientId(client_id) {
|
|
43
34
|
}
|
44
35
|
}
|
45
36
|
|
46
|
-
const authorize = async (
|
37
|
+
const authorize = async (body) => {
|
47
38
|
const {
|
48
39
|
response_type = 'code',
|
49
40
|
client_id,
|
@@ -55,16 +46,17 @@ const authorize = async (req, res) => {
|
|
55
46
|
code_challenge_method = '',
|
56
47
|
// eslint-disable-next-line no-unused-vars
|
57
48
|
provider = 'saml',
|
58
|
-
} =
|
49
|
+
} = body;
|
59
50
|
|
60
51
|
if (!redirect_uri) {
|
61
|
-
|
52
|
+
throw new JacksonError('Please specify a redirect URL.', 400);
|
62
53
|
}
|
63
54
|
|
64
55
|
if (!state) {
|
65
|
-
|
66
|
-
.
|
67
|
-
|
56
|
+
throw new JacksonError(
|
57
|
+
'Please specify a state to safeguard against XSRF attacks.',
|
58
|
+
400
|
59
|
+
);
|
68
60
|
}
|
69
61
|
|
70
62
|
let samlConfig;
|
@@ -84,7 +76,7 @@ const authorize = async (req, res) => {
|
|
84
76
|
});
|
85
77
|
|
86
78
|
if (!samlConfigs || samlConfigs.length === 0) {
|
87
|
-
|
79
|
+
throw new JacksonError('SAML configuration not found.', 403);
|
88
80
|
}
|
89
81
|
|
90
82
|
// TODO: Support multiple matches
|
@@ -99,7 +91,7 @@ const authorize = async (req, res) => {
|
|
99
91
|
});
|
100
92
|
|
101
93
|
if (!samlConfigs || samlConfigs.length === 0) {
|
102
|
-
|
94
|
+
throw new JacksonError('SAML configuration not found.', 403);
|
103
95
|
}
|
104
96
|
|
105
97
|
// TODO: Support multiple matches
|
@@ -107,15 +99,15 @@ const authorize = async (req, res) => {
|
|
107
99
|
}
|
108
100
|
|
109
101
|
if (!samlConfig) {
|
110
|
-
|
102
|
+
throw new JacksonError('SAML configuration not found.', 403);
|
111
103
|
}
|
112
104
|
|
113
105
|
if (!allowed.redirect(redirect_uri, samlConfig.redirectUrl)) {
|
114
|
-
|
106
|
+
throw new JacksonError('Redirect URL is not allowed.', 403);
|
115
107
|
}
|
116
108
|
|
117
109
|
const samlReq = saml.request({
|
118
|
-
entityID:
|
110
|
+
entityID: options.samlAudience,
|
119
111
|
callbackUrl: options.externalUrl + options.samlPath,
|
120
112
|
signingKey: samlConfig.certs.privateKey,
|
121
113
|
});
|
@@ -131,24 +123,26 @@ const authorize = async (req, res) => {
|
|
131
123
|
code_challenge_method,
|
132
124
|
});
|
133
125
|
|
134
|
-
|
126
|
+
const redirectUrl = redirect.success(samlConfig.idpMetadata.sso.redirectUrl, {
|
135
127
|
RelayState: relayStatePrefix + sessionId,
|
136
128
|
SAMLRequest: Buffer.from(samlReq.request).toString('base64'),
|
137
129
|
});
|
130
|
+
|
131
|
+
return { redirect_url: redirectUrl };
|
138
132
|
};
|
139
133
|
|
140
|
-
const samlResponse = async (
|
141
|
-
const { SAMLResponse } =
|
134
|
+
const samlResponse = async (body) => {
|
135
|
+
const { SAMLResponse } = body; // RelayState will contain the sessionId from earlier quasi-oauth flow
|
142
136
|
|
143
|
-
let RelayState =
|
137
|
+
let RelayState = body.RelayState || '';
|
144
138
|
|
145
139
|
if (!options.idpEnabled && !RelayState.startsWith(relayStatePrefix)) {
|
146
140
|
// IDP is disabled so block the request
|
147
|
-
|
148
|
-
|
149
|
-
.
|
150
|
-
|
151
|
-
|
141
|
+
|
142
|
+
throw new JacksonError(
|
143
|
+
'IdP (Identity Provider) flow has been disabled. Please head to your Service Provider to login.',
|
144
|
+
403
|
145
|
+
);
|
152
146
|
}
|
153
147
|
|
154
148
|
if (!RelayState.startsWith(relayStatePrefix)) {
|
@@ -167,7 +161,7 @@ const samlResponse = async (req, res) => {
|
|
167
161
|
});
|
168
162
|
|
169
163
|
if (!samlConfigs || samlConfigs.length === 0) {
|
170
|
-
|
164
|
+
throw new JacksonError('SAML configuration not found.', 403);
|
171
165
|
}
|
172
166
|
|
173
167
|
// TODO: Support multiple matches
|
@@ -178,10 +172,9 @@ const samlResponse = async (req, res) => {
|
|
178
172
|
if (RelayState !== '') {
|
179
173
|
session = await sessionStore.get(RelayState);
|
180
174
|
if (!session) {
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
'Unable to validate state from the origin request.'
|
175
|
+
throw new JacksonError(
|
176
|
+
'Unable to validate state from the origin request.',
|
177
|
+
403
|
185
178
|
);
|
186
179
|
}
|
187
180
|
}
|
@@ -217,7 +210,7 @@ const samlResponse = async (req, res) => {
|
|
217
210
|
session.redirect_uri &&
|
218
211
|
!allowed.redirect(session.redirect_uri, samlConfig.redirectUrl)
|
219
212
|
) {
|
220
|
-
|
213
|
+
throw new JacksonError('Redirect URL is not allowed.', 403);
|
221
214
|
}
|
222
215
|
|
223
216
|
let params = {
|
@@ -228,33 +221,34 @@ const samlResponse = async (req, res) => {
|
|
228
221
|
params.state = session.state;
|
229
222
|
}
|
230
223
|
|
231
|
-
|
232
|
-
res,
|
224
|
+
const redirectUrl = redirect.success(
|
233
225
|
(session && session.redirect_uri) || samlConfig.defaultRedirectUrl,
|
234
226
|
params
|
235
227
|
);
|
228
|
+
|
229
|
+
return { redirect_url: redirectUrl };
|
236
230
|
};
|
237
231
|
|
238
|
-
const token = async (
|
232
|
+
const token = async (body) => {
|
239
233
|
const {
|
240
234
|
client_id,
|
241
235
|
client_secret,
|
242
236
|
code_verifier,
|
243
237
|
code,
|
244
238
|
grant_type = 'authorization_code',
|
245
|
-
} =
|
239
|
+
} = body;
|
246
240
|
|
247
241
|
if (grant_type !== 'authorization_code') {
|
248
|
-
|
242
|
+
throw new JacksonError('Unsupported grant_type', 400);
|
249
243
|
}
|
250
244
|
|
251
245
|
if (!code) {
|
252
|
-
|
246
|
+
throw new JacksonError('Please specify code', 400);
|
253
247
|
}
|
254
248
|
|
255
249
|
const codeVal = await codeStore.get(code);
|
256
250
|
if (!codeVal || !codeVal.profile) {
|
257
|
-
|
251
|
+
throw new JacksonError('Invalid code', 403);
|
258
252
|
}
|
259
253
|
|
260
254
|
if (client_id && client_secret) {
|
@@ -266,7 +260,7 @@ const token = async (req, res) => {
|
|
266
260
|
client_id !== codeVal.clientID ||
|
267
261
|
client_secret !== codeVal.clientSecret
|
268
262
|
) {
|
269
|
-
|
263
|
+
throw new JacksonError('Invalid client_id or client_secret', 401);
|
270
264
|
}
|
271
265
|
}
|
272
266
|
} else if (code_verifier) {
|
@@ -277,12 +271,13 @@ const token = async (req, res) => {
|
|
277
271
|
}
|
278
272
|
|
279
273
|
if (codeVal.session.code_challenge !== cv) {
|
280
|
-
|
274
|
+
throw new JacksonError('Invalid code_verifier', 401);
|
281
275
|
}
|
282
276
|
} else if (codeVal && codeVal.session) {
|
283
|
-
|
284
|
-
|
285
|
-
|
277
|
+
throw new JacksonError(
|
278
|
+
'Please specify client_secret or code_verifier',
|
279
|
+
401
|
280
|
+
);
|
286
281
|
}
|
287
282
|
|
288
283
|
// store details against a token
|
@@ -290,24 +285,17 @@ const token = async (req, res) => {
|
|
290
285
|
|
291
286
|
await tokenStore.put(token, codeVal.profile);
|
292
287
|
|
293
|
-
|
288
|
+
return {
|
294
289
|
access_token: token,
|
295
290
|
token_type: 'bearer',
|
296
291
|
expires_in: options.db.ttl,
|
297
|
-
}
|
292
|
+
};
|
298
293
|
};
|
299
294
|
|
300
|
-
const userInfo = async (
|
301
|
-
|
302
|
-
|
303
|
-
// check for query param
|
304
|
-
if (!token) {
|
305
|
-
token = req.query.access_token;
|
306
|
-
}
|
307
|
-
|
308
|
-
const profile = await tokenStore.get(token);
|
295
|
+
const userInfo = async (token) => {
|
296
|
+
const { claims } = await tokenStore.get(token);
|
309
297
|
|
310
|
-
|
298
|
+
return claims;
|
311
299
|
};
|
312
300
|
|
313
301
|
module.exports = (opts) => {
|
package/src/controller/utils.js
CHANGED
@@ -3,6 +3,17 @@ const indexNames = {
|
|
3
3
|
tenantProduct: 'tenantProduct',
|
4
4
|
};
|
5
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
|
+
|
6
16
|
module.exports = {
|
7
17
|
indexNames,
|
18
|
+
extractAuthToken,
|
8
19
|
};
|
package/src/db/db.test.js
CHANGED
@@ -224,7 +224,7 @@ t.test('dbs', ({ end }) => {
|
|
224
224
|
}
|
225
225
|
|
226
226
|
await new Promise((resolve) =>
|
227
|
-
setTimeout(resolve, (
|
227
|
+
setTimeout(resolve, (2*ttl + 0.5) * 1000)
|
228
228
|
);
|
229
229
|
|
230
230
|
const ret1 = await ttlStore.get(record1.id);
|
@@ -0,0 +1,23 @@
|
|
1
|
+
const EntitySchema = require('typeorm').EntitySchema;
|
2
|
+
const JacksonTTL = require('../model/JacksonTTL.js');
|
3
|
+
|
4
|
+
module.exports = new EntitySchema({
|
5
|
+
name: 'JacksonTTL',
|
6
|
+
target: JacksonTTL,
|
7
|
+
columns: {
|
8
|
+
key: {
|
9
|
+
primary: true,
|
10
|
+
type: 'varchar',
|
11
|
+
length: 1500,
|
12
|
+
},
|
13
|
+
expiresAt: {
|
14
|
+
type: 'bigint',
|
15
|
+
},
|
16
|
+
},
|
17
|
+
indices: [
|
18
|
+
{
|
19
|
+
name: '_jackson_ttl_expires_at',
|
20
|
+
columns: ['expiresAt'],
|
21
|
+
},
|
22
|
+
],
|
23
|
+
});
|
package/src/db/sql/sql.js
CHANGED
@@ -2,6 +2,7 @@ require('reflect-metadata');
|
|
2
2
|
const typeorm = require('typeorm');
|
3
3
|
const JacksonStore = require('./model/JacksonStore.js');
|
4
4
|
const JacksonIndex = require('./model/JacksonIndex.js');
|
5
|
+
const JacksonTTL = require('./model/JacksonTTL.js');
|
5
6
|
|
6
7
|
const dbutils = require('../utils.js');
|
7
8
|
|
@@ -20,6 +21,7 @@ class Sql {
|
|
20
21
|
entities: [
|
21
22
|
require('./entity/JacksonStore.js')(options.type),
|
22
23
|
require('./entity/JacksonIndex.js'),
|
24
|
+
require('./entity/JacksonTTL.js'),
|
23
25
|
],
|
24
26
|
});
|
25
27
|
|
@@ -33,22 +35,29 @@ class Sql {
|
|
33
35
|
|
34
36
|
this.storeRepository = this.connection.getRepository(JacksonStore);
|
35
37
|
this.indexRepository = this.connection.getRepository(JacksonIndex);
|
38
|
+
this.ttlRepository = this.connection.getRepository(JacksonTTL);
|
36
39
|
|
37
40
|
if (options.ttl && options.limit) {
|
38
41
|
this.ttlCleanup = async () => {
|
39
42
|
const now = Date.now();
|
40
43
|
|
41
44
|
while (true) {
|
42
|
-
const ids = await this.
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
const ids = await this.ttlRepository
|
46
|
+
.createQueryBuilder('jackson_ttl')
|
47
|
+
.limit(options.limit)
|
48
|
+
.where('jackson_ttl.expiresAt <= :expiresAt', { expiresAt: now })
|
49
|
+
.getMany();
|
46
50
|
|
47
51
|
if (ids.length <= 0) {
|
48
52
|
break;
|
49
53
|
}
|
50
54
|
|
55
|
+
const delIds = ids.map((id) => {
|
56
|
+
return id.key;
|
57
|
+
});
|
58
|
+
|
51
59
|
await this.storeRepository.remove(ids);
|
60
|
+
await this.ttlRepository.delete(delIds);
|
52
61
|
}
|
53
62
|
|
54
63
|
this.timerId = setTimeout(this.ttlCleanup, options.ttl * 1000);
|
@@ -99,13 +108,15 @@ class Sql {
|
|
99
108
|
|
100
109
|
async put(namespace, key, val, ttl = 0, ...indexes) {
|
101
110
|
await this.connection.transaction(async (transactionalEntityManager) => {
|
102
|
-
const
|
103
|
-
|
104
|
-
JSON.stringify(val),
|
105
|
-
ttl > 0 ? Date.now() + ttl * 1000 : null
|
106
|
-
);
|
111
|
+
const dbKey = dbutils.key(namespace, key);
|
112
|
+
const store = new JacksonStore(dbKey, JSON.stringify(val));
|
107
113
|
await transactionalEntityManager.save(store);
|
108
114
|
|
115
|
+
if (ttl) {
|
116
|
+
const ttlRec = new JacksonTTL(dbKey, Date.now() + ttl * 1000);
|
117
|
+
await transactionalEntityManager.save(ttlRec);
|
118
|
+
}
|
119
|
+
|
109
120
|
// no ttl support for secondary indexes
|
110
121
|
for (const idx of indexes || []) {
|
111
122
|
const key = dbutils.keyForIndex(namespace, idx);
|
package/src/env.js
CHANGED
@@ -7,6 +7,8 @@ const samlPath = process.env.SAML_PATH || '/oauth/saml';
|
|
7
7
|
const internalHostUrl = process.env.INTERNAL_HOST_URL || 'localhost';
|
8
8
|
const internalHostPort = (process.env.INTERNAL_HOST_PORT || '6000') * 1;
|
9
9
|
|
10
|
+
const apiKeys = (process.env.JACKSON_API_KEYS || '').split(',');
|
11
|
+
|
10
12
|
const samlAudience = process.env.SAML_AUDIENCE;
|
11
13
|
const preLoadedConfig = process.env.PRE_LOADED_CONFIG;
|
12
14
|
|
@@ -27,6 +29,7 @@ module.exports = {
|
|
27
29
|
preLoadedConfig,
|
28
30
|
internalHostUrl,
|
29
31
|
internalHostPort,
|
32
|
+
apiKeys,
|
30
33
|
idpEnabled,
|
31
34
|
db,
|
32
35
|
useInternalServer: !(
|
package/src/error.js
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class JacksonError extends Error {
|
2
|
+
constructor(message, statusCode = 500) {
|
3
|
+
super(message);
|
4
|
+
|
5
|
+
this.name = this.constructor.name;
|
6
|
+
this.statusCode = statusCode;
|
7
|
+
|
8
|
+
Error.captureStackTrace(this, this.constructor);
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
module.exports = { JacksonError };
|
package/src/index.js
CHANGED
@@ -19,7 +19,7 @@ const defaultOpts = (opts) => {
|
|
19
19
|
newOpts.db = newOpts.db || {};
|
20
20
|
newOpts.db.engine = newOpts.db.engine || 'sql'; // Supported values: redis, sql, mongo, mem. Keep comment in sync with db.js
|
21
21
|
newOpts.db.url =
|
22
|
-
newOpts.db.url || '
|
22
|
+
newOpts.db.url || 'postgresql://postgres:postgres@localhost:5432/postgres';
|
23
23
|
newOpts.db.type = newOpts.db.type || 'postgres'; // Only needed if DB_ENGINE is sql. Supported values: postgres, cockroachdb, mysql, mariadb
|
24
24
|
newOpts.db.ttl = (newOpts.db.ttl || 300) * 1; // TTL for the code, session and token stores (in seconds)
|
25
25
|
newOpts.db.limit = (newOpts.db.limit || 1000) * 1; // Limit ttl cleanup to this many items at a time
|
@@ -56,7 +56,7 @@ module.exports = async function (opts) {
|
|
56
56
|
}
|
57
57
|
}
|
58
58
|
|
59
|
-
const type = opts.db.type ? ' Type: ' + opts.db.type : '';
|
59
|
+
const type = opts.db.engine === 'sql' && opts.db.type ? ' Type: ' + opts.db.type : '';
|
60
60
|
console.log(`Using engine: ${opts.db.engine}.${type}`);
|
61
61
|
|
62
62
|
return {
|
package/src/jackson.js
CHANGED
@@ -2,6 +2,7 @@ const express = require('express');
|
|
2
2
|
const cors = require('cors');
|
3
3
|
|
4
4
|
const env = require('./env.js');
|
5
|
+
const { extractAuthToken } = require('./controller/utils.js');
|
5
6
|
|
6
7
|
let apiController;
|
7
8
|
let oauthController;
|
@@ -16,33 +17,60 @@ app.use(express.urlencoded({ extended: true }));
|
|
16
17
|
|
17
18
|
app.get(oauthPath + '/authorize', async (req, res) => {
|
18
19
|
try {
|
19
|
-
await oauthController.authorize(req
|
20
|
+
const { redirect_url } = await oauthController.authorize(req.query);
|
21
|
+
|
22
|
+
res.redirect(redirect_url);
|
20
23
|
} catch (err) {
|
21
|
-
|
24
|
+
const { message, statusCode = 500 } = err;
|
25
|
+
|
26
|
+
res.status(statusCode).send(message);
|
22
27
|
}
|
23
28
|
});
|
24
29
|
|
25
30
|
app.post(env.samlPath, async (req, res) => {
|
26
31
|
try {
|
27
|
-
await oauthController.samlResponse(req
|
32
|
+
const { redirect_url } = await oauthController.samlResponse(req.body);
|
33
|
+
|
34
|
+
res.redirect(redirect_url);
|
28
35
|
} catch (err) {
|
29
|
-
|
36
|
+
const { message, statusCode = 500 } = err;
|
37
|
+
|
38
|
+
res.status(statusCode).send(message);
|
30
39
|
}
|
31
40
|
});
|
32
41
|
|
33
42
|
app.post(oauthPath + '/token', cors(), async (req, res) => {
|
34
43
|
try {
|
35
|
-
await oauthController.token(req
|
44
|
+
const result = await oauthController.token(req.body);
|
45
|
+
|
46
|
+
res.send(result);
|
36
47
|
} catch (err) {
|
37
|
-
|
48
|
+
const { message, statusCode = 500 } = err;
|
49
|
+
|
50
|
+
res.status(statusCode).send(message);
|
38
51
|
}
|
39
52
|
});
|
40
53
|
|
41
|
-
app.get(oauthPath + '/userinfo',
|
54
|
+
app.get(oauthPath + '/userinfo', async (req, res) => {
|
42
55
|
try {
|
43
|
-
|
56
|
+
let token = extractAuthToken(req);
|
57
|
+
|
58
|
+
// check for query param
|
59
|
+
if (!token) {
|
60
|
+
token = req.query.access_token;
|
61
|
+
}
|
62
|
+
|
63
|
+
if (!token) {
|
64
|
+
res.status(401).json({ message: 'Unauthorized' });
|
65
|
+
}
|
66
|
+
|
67
|
+
const profile = await oauthController.userInfo(token);
|
68
|
+
|
69
|
+
res.json(profile);
|
44
70
|
} catch (err) {
|
45
|
-
|
71
|
+
const { message, statusCode = 500 } = err;
|
72
|
+
|
73
|
+
res.status(statusCode).json({ message });
|
46
74
|
}
|
47
75
|
});
|
48
76
|
|
@@ -66,8 +94,18 @@ if (env.useInternalServer) {
|
|
66
94
|
internalApp.use(express.urlencoded({ extended: true }));
|
67
95
|
}
|
68
96
|
|
97
|
+
const validateApiKey = (token) => {
|
98
|
+
return env.apiKeys.includes(token);
|
99
|
+
};
|
100
|
+
|
69
101
|
internalApp.post(apiPath + '/config', async (req, res) => {
|
70
102
|
try {
|
103
|
+
const apiKey = extractAuthToken(req);
|
104
|
+
if (!validateApiKey(apiKey)) {
|
105
|
+
res.status(401).send('Unauthorized');
|
106
|
+
return;
|
107
|
+
}
|
108
|
+
|
71
109
|
res.json(await apiController.config(req.body));
|
72
110
|
} catch (err) {
|
73
111
|
res.status(500).json({
|
@@ -76,6 +114,22 @@ internalApp.post(apiPath + '/config', async (req, res) => {
|
|
76
114
|
}
|
77
115
|
});
|
78
116
|
|
117
|
+
internalApp.post(apiPath + '/config/get', async (req, res) => {
|
118
|
+
try {
|
119
|
+
const apiKey = extractAuthToken(req);
|
120
|
+
if (!validateApiKey(apiKey)) {
|
121
|
+
res.status(401).send('Unauthorized');
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
|
125
|
+
res.json(await apiController.getConfig(req.body));
|
126
|
+
} catch (err) {
|
127
|
+
res.status(500).json({
|
128
|
+
error: err.message,
|
129
|
+
});
|
130
|
+
}
|
131
|
+
});
|
132
|
+
|
79
133
|
let internalServer = server;
|
80
134
|
if (env.useInternalServer) {
|
81
135
|
internalServer = internalApp.listen(env.internalHostPort, async () => {
|
@@ -0,0 +1,40 @@
|
|
1
|
+
const mapping = [
|
2
|
+
{
|
3
|
+
attribute: 'id',
|
4
|
+
schema:
|
5
|
+
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',
|
6
|
+
},
|
7
|
+
{
|
8
|
+
attribute: 'email',
|
9
|
+
schema:
|
10
|
+
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
11
|
+
},
|
12
|
+
{
|
13
|
+
attribute: 'firstName',
|
14
|
+
schema: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
|
15
|
+
},
|
16
|
+
{
|
17
|
+
attribute: 'lastName',
|
18
|
+
schema: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
|
19
|
+
},
|
20
|
+
];
|
21
|
+
|
22
|
+
const map = (claims) => {
|
23
|
+
const profile = {
|
24
|
+
raw: claims,
|
25
|
+
};
|
26
|
+
|
27
|
+
mapping.forEach((m) => {
|
28
|
+
if (claims[m.attribute]) {
|
29
|
+
profile[m.attribute] = claims[m.attribute];
|
30
|
+
} else if (claims[m.schema]) {
|
31
|
+
profile[m.attribute] = claims[m.schema];
|
32
|
+
}
|
33
|
+
});
|
34
|
+
|
35
|
+
return profile;
|
36
|
+
};
|
37
|
+
|
38
|
+
module.exports = {
|
39
|
+
map,
|
40
|
+
};
|