@boxyhq/saml-jackson 0.2.3-beta.223 → 0.2.3-beta.227

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxyhq/saml-jackson",
3
- "version": "0.2.3-beta.223",
3
+ "version": "0.2.3-beta.227",
4
4
  "license": "Apache 2.0",
5
5
  "description": "SAML 2.0 service",
6
6
  "main": "dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "build": "tsc -p tsconfig.build.json",
20
20
  "start": "cross-env IDP_ENABLED=true node dist/jackson.js",
21
21
  "dev": "cross-env IDP_ENABLED=true nodemon --config nodemon.json src/jackson.ts",
22
- "mongo": "cross-env JACKSON_API_KEYS=secret DB_ENGINE=mongo DB_URL=mongodb://localhost:27017/jackson DB_ENCRYPTION_KEY=RiVoTxDoLUUoIUOp224abMxK6PGGfFuF nodemon --config nodemon.json src/jackson.ts",
23
- "sql": "cross-env JACKSON_API_KEYS=secret DB_ENGINE=sql DB_TYPE=postgres DB_URL=postgres://postgres:postgres@localhost:5432/jackson DB_ENCRYPTION_KEY=RiVoTxDoLUUoIUOp224abMxK6PGGfFuF nodemon --config nodemon.json src/jackson.ts",
22
+ "mongo": "cross-env JACKSON_API_KEYS=secret DB_ENGINE=mongo DB_URL=mongodb://localhost:27017/jackson nodemon --config nodemon.json src/jackson.ts",
23
+ "sql": "cross-env JACKSON_API_KEYS=secret DB_ENGINE=sql DB_TYPE=postgres DB_URL=postgres://postgres:postgres@localhost:5432/jackson nodemon --config nodemon.json src/jackson.ts",
24
24
  "pre-loaded": "cross-env JACKSON_API_KEYS=secret DB_ENGINE=mem PRE_LOADED_CONFIG='./_config' nodemon --config nodemon.json src/jackson.ts",
25
25
  "pre-loaded-db": "cross-env JACKSON_API_KEYS=secret PRE_LOADED_CONFIG='./_config' nodemon --config nodemon.json src/jackson.ts",
26
26
  "test": "tap --ts --timeout=100 src/**/*.test.ts",
package/src/db/mem.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // This is an in-memory implementation to be used with testing and prototyping only
2
2
 
3
- import { DatabaseDriver, DatabaseOption, Index } from 'saml-jackson';
3
+ import { DatabaseDriver, DatabaseOption, Index, Encrypted } from 'saml-jackson';
4
4
  import * as dbutils from './utils';
5
5
 
6
6
  class Mem implements DatabaseDriver {
@@ -65,7 +65,7 @@ class Mem implements DatabaseDriver {
65
65
  async put(
66
66
  namespace: string,
67
67
  key: string,
68
- val: string,
68
+ val: Encrypted,
69
69
  ttl: number = 0,
70
70
  ...indexes: any[]
71
71
  ): Promise<any> {
package/src/db/mongo.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { MongoClient } from 'mongodb';
2
- import { DatabaseDriver, DatabaseOption, Index } from 'saml-jackson';
2
+ import { DatabaseDriver, DatabaseOption, Encrypted, Index } from 'saml-jackson';
3
3
  import * as dbutils from './utils';
4
4
 
5
5
  type Document = {
6
- value: string;
6
+ value: Encrypted;
7
7
  expiresAt: Date;
8
8
  indexes: string[];
9
9
  };
@@ -64,7 +64,7 @@ class Mongo implements DatabaseDriver {
64
64
  async put(
65
65
  namespace: string,
66
66
  key: string,
67
- val: string,
67
+ val: Encrypted,
68
68
  ttl: number = 0,
69
69
  ...indexes: any[]
70
70
  ): Promise<void> {
package/src/db/redis.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as redis from 'redis';
2
- import { DatabaseDriver, DatabaseOption, Index } from 'saml-jackson';
2
+ import { DatabaseDriver, DatabaseOption, Encrypted, Index } from 'saml-jackson';
3
3
  import * as dbutils from './utils';
4
4
 
5
5
  class Redis implements DatabaseDriver {
@@ -54,7 +54,7 @@ class Redis implements DatabaseDriver {
54
54
  async put(
55
55
  namespace: string,
56
56
  key: string,
57
- val: string,
57
+ val: Encrypted,
58
58
  ttl: number = 0,
59
59
  ...indexes: any[]
60
60
  ): Promise<void> {
@@ -1,8 +1,8 @@
1
1
  export class JacksonStore {
2
2
  constructor(
3
3
  public key: string,
4
- public value: any,
5
- public iv: any,
6
- public tag: any
4
+ public value: string,
5
+ public iv?: string,
6
+ public tag?: string
7
7
  ) {}
8
8
  }
package/src/db/sql/sql.ts CHANGED
@@ -2,23 +2,24 @@
2
2
 
3
3
  require('reflect-metadata');
4
4
 
5
- import { DatabaseDriver, DatabaseOption, Index } from 'saml-jackson';
5
+ import { DatabaseDriver, DatabaseOption, Index, Encrypted } from 'saml-jackson';
6
6
  import { Connection, createConnection } from 'typeorm';
7
7
  import * as dbutils from '../utils';
8
+ import JacksonIndexEntity from './entity/JacksonIndex';
9
+ import JacksonStoreEntity from './entity/JacksonStore';
10
+ import {
11
+ JacksonTTL,
12
+ JacksonTTL as JacksonTTLEntity,
13
+ } from './entity/JacksonTTL';
8
14
  import { JacksonIndex } from './model/JacksonIndex';
9
15
  import { JacksonStore } from './model/JacksonStore';
10
- import { JacksonTTL } from './entity/JacksonTTL';
11
-
12
- import JacksonStoreEntity from './entity/JacksonStore';
13
- import JacksonIndexEntity from './entity/JacksonIndex';
14
- import { JacksonTTL as JacksonTTLEntity } from './entity/JacksonTTL';
15
16
 
16
17
  class Sql implements DatabaseDriver {
17
18
  private options: DatabaseOption;
18
19
  private connection!: Connection;
19
- private storeRepository; //!: typeorm.Repository<JacksonStore>;
20
- private indexRepository; //!: typeorm.Repository<JacksonIndex>;
21
- private ttlRepository; //!: typeorm.Repository<JacksonTTL>;
20
+ private storeRepository;
21
+ private indexRepository;
22
+ private ttlRepository;
22
23
  private ttlCleanup;
23
24
  private timerId;
24
25
 
@@ -29,8 +30,6 @@ class Sql implements DatabaseDriver {
29
30
  async init(): Promise<Sql> {
30
31
  while (true) {
31
32
  try {
32
- // TODO: Fix it
33
- // @ts-ignore
34
33
  this.connection = await createConnection({
35
34
  name: this.options.type + Math.floor(Math.random() * 100000),
36
35
  type: this.options.type,
@@ -116,11 +115,10 @@ class Sql implements DatabaseDriver {
116
115
  key: dbutils.keyForIndex(namespace, idx),
117
116
  });
118
117
 
119
- const ret: string[] = [];
118
+ const ret: Encrypted[] = [];
120
119
 
121
120
  if (res) {
122
121
  res.forEach((r) => {
123
- // @ts-ignore
124
122
  ret.push({
125
123
  value: r.store.value,
126
124
  iv: r.store.iv,
@@ -135,14 +133,13 @@ class Sql implements DatabaseDriver {
135
133
  async put(
136
134
  namespace: string,
137
135
  key: string,
138
- val: string,
136
+ val: Encrypted,
139
137
  ttl: number = 0,
140
138
  ...indexes: any[]
141
139
  ): Promise<void> {
142
140
  await this.connection.transaction(async (transactionalEntityManager) => {
143
141
  const dbKey = dbutils.key(namespace, key);
144
142
 
145
- // @ts-ignore
146
143
  const store = new JacksonStore(dbKey, val.value, val.iv, val.tag);
147
144
 
148
145
  await transactionalEntityManager.save(store);
package/src/jackson.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import cors from 'cors';
2
2
  import express from 'express';
3
+ import { IOAuthController, ISAMLConfig } from 'saml-jackson';
3
4
  import { JacksonError } from './controller/error';
4
5
  import { extractAuthToken } from './controller/utils';
5
6
  import env from './env';
6
7
  import jackson from './index';
7
8
 
8
- let apiController;
9
- let oauthController;
9
+ let apiController: ISAMLConfig;
10
+ let oauthController: IOAuthController;
10
11
 
11
12
  const oauthPath = '/oauth';
12
13
  const apiPath = '/api/v1/saml';
@@ -62,7 +63,7 @@ app.get(oauthPath + '/userinfo', async (req, res) => {
62
63
  }
63
64
 
64
65
  if (!token) {
65
- res.status(401).json({ message: 'Unauthorized' });
66
+ return res.status(401).json({ message: 'Unauthorized' });
66
67
  }
67
68
 
68
69
  const profile = await oauthController.userInfo(token);
@@ -80,7 +81,6 @@ const server = app.listen(env.hostPort, async () => {
80
81
  `🚀 The path of the righteous server: http://${env.hostUrl}:${env.hostPort}`
81
82
  );
82
83
 
83
- // TODO: Fix it
84
84
  // @ts-ignore
85
85
  const ctrlrModule = await jackson(env);
86
86
 
@@ -16,8 +16,7 @@ const OPTIONS = {
16
16
  samlAudience: 'https://saml.boxyhq.com',
17
17
  samlPath: '/sso/oauth/saml',
18
18
  db: {
19
- engine: 'mongo',
20
- url: 'mongodb://localhost:27017/jackson-demo',
19
+ engine: 'mem',
21
20
  } as DatabaseOption,
22
21
  };
23
22
 
@@ -1,30 +1,36 @@
1
1
  import crypto from 'crypto';
2
2
  import { promises as fs } from 'fs';
3
3
  import path from 'path';
4
- import { JacksonOption } from 'saml-jackson';
4
+ import {
5
+ IOAuthController,
6
+ ISAMLConfig,
7
+ JacksonOption,
8
+ OAuthReqBody,
9
+ OAuthTokenReq,
10
+ SAMLResponsePayload,
11
+ } from 'saml-jackson';
5
12
  import sinon from 'sinon';
6
13
  import tap from 'tap';
7
14
  import { JacksonError } from '../controller/error';
8
15
  import readConfig from '../read-config';
9
16
  import saml from '../saml/saml';
10
17
 
11
- // TODO: Add type
12
- let apiController;
13
- let oauthController;
18
+ let apiController: ISAMLConfig;
19
+ let oauthController: IOAuthController;
14
20
 
15
21
  const code = '1234567890';
16
22
  const token = '24c1550190dd6a5a9bd6fe2a8ff69d593121c7b9';
17
23
 
18
24
  const metadataPath = path.join(__dirname, '/data/metadata');
19
25
 
20
- const options = {
26
+ const options = <JacksonOption>{
21
27
  externalUrl: 'https://my-cool-app.com',
22
28
  samlAudience: 'https://saml.boxyhq.com',
23
29
  samlPath: '/sso/oauth/saml',
24
30
  db: {
25
31
  engine: 'mem',
26
32
  },
27
- } as JacksonOption;
33
+ };
28
34
 
29
35
  const samlConfig = {
30
36
  tenant: 'boxyhq.com',
@@ -57,13 +63,13 @@ tap.teardown(async () => {
57
63
 
58
64
  tap.test('authorize()', async (t) => {
59
65
  t.test('Should throw an error if `redirect_uri` null', async (t) => {
60
- const body = {
61
- redirect_uri: null,
66
+ const body: Partial<OAuthReqBody> = {
67
+ redirect_uri: undefined,
62
68
  state: 'state',
63
69
  };
64
70
 
65
71
  try {
66
- await oauthController.authorize(body);
72
+ await oauthController.authorize(<OAuthReqBody>body);
67
73
  t.fail('Expecting JacksonError.');
68
74
  } catch (err) {
69
75
  const { message, statusCode } = err as JacksonError;
@@ -79,13 +85,13 @@ tap.test('authorize()', async (t) => {
79
85
  });
80
86
 
81
87
  t.test('Should throw an error if `state` null', async (t) => {
82
- const body = {
88
+ const body: Partial<OAuthReqBody> = {
83
89
  redirect_uri: 'https://example.com/',
84
- state: null,
90
+ state: undefined,
85
91
  };
86
92
 
87
93
  try {
88
- await oauthController.authorize(body);
94
+ await oauthController.authorize(<OAuthReqBody>body);
89
95
 
90
96
  t.fail('Expecting JacksonError.');
91
97
  } catch (err) {
@@ -109,7 +115,7 @@ tap.test('authorize()', async (t) => {
109
115
  };
110
116
 
111
117
  try {
112
- await oauthController.authorize(body);
118
+ await oauthController.authorize(<OAuthReqBody>body);
113
119
 
114
120
  t.fail('Expecting JacksonError.');
115
121
  } catch (err) {
@@ -135,7 +141,7 @@ tap.test('authorize()', async (t) => {
135
141
  };
136
142
 
137
143
  try {
138
- await oauthController.authorize(body);
144
+ await oauthController.authorize(<OAuthReqBody>body);
139
145
 
140
146
  t.fail('Expecting JacksonError.');
141
147
  } catch (err) {
@@ -159,7 +165,7 @@ tap.test('authorize()', async (t) => {
159
165
  client_id: `tenant=${samlConfig.tenant}&product=${samlConfig.product}`,
160
166
  };
161
167
 
162
- const response = await oauthController.authorize(body);
168
+ const response = await oauthController.authorize(<OAuthReqBody>body);
163
169
  const params = new URLSearchParams(new URL(response.redirect_url).search);
164
170
 
165
171
  t.ok('redirect_url' in response, 'got the Idp authorize URL');
@@ -179,7 +185,9 @@ tap.test('samlResponse()', async (t) => {
179
185
  client_id: `tenant=${samlConfig.tenant}&product=${samlConfig.product}`,
180
186
  };
181
187
 
182
- const { redirect_url } = await oauthController.authorize(authBody);
188
+ const { redirect_url } = await oauthController.authorize(
189
+ <OAuthReqBody>authBody
190
+ );
183
191
 
184
192
  const relayState = new URLSearchParams(new URL(redirect_url).search).get(
185
193
  'RelayState'
@@ -191,12 +199,12 @@ tap.test('samlResponse()', async (t) => {
191
199
  );
192
200
 
193
201
  t.test('Should throw an error if `RelayState` is missing', async (t) => {
194
- const responseBody = {
202
+ const responseBody: Partial<SAMLResponsePayload> = {
195
203
  SAMLResponse: rawResponse,
196
204
  };
197
205
 
198
206
  try {
199
- await oauthController.samlResponse(responseBody);
207
+ await oauthController.samlResponse(<SAMLResponsePayload>responseBody);
200
208
 
201
209
  t.fail('Expecting JacksonError.');
202
210
  } catch (err) {
@@ -224,10 +232,13 @@ tap.test('samlResponse()', async (t) => {
224
232
  const stubValidateAsync = sinon
225
233
  .stub(saml, 'validateAsync')
226
234
  .resolves({ audience: '', claims: {}, issuer: '', sessionIndex: '' });
235
+
227
236
  //@ts-ignore
228
237
  const stubRandomBytes = sinon.stub(crypto, 'randomBytes').returns(code);
229
238
 
230
- const response = await oauthController.samlResponse(responseBody);
239
+ const response = await oauthController.samlResponse(
240
+ <SAMLResponsePayload>responseBody
241
+ );
231
242
 
232
243
  const params = new URLSearchParams(new URL(response.redirect_url).search);
233
244
 
@@ -257,7 +268,7 @@ tap.test('token()', (t) => {
257
268
  };
258
269
 
259
270
  try {
260
- await oauthController.token(body);
271
+ await oauthController.token(<OAuthTokenReq>body);
261
272
 
262
273
  t.fail('Expecting JacksonError.');
263
274
  } catch (err) {
@@ -280,7 +291,7 @@ tap.test('token()', (t) => {
280
291
  };
281
292
 
282
293
  try {
283
- await oauthController.token(body);
294
+ await oauthController.token(<OAuthTokenReq>body);
284
295
 
285
296
  t.fail('Expecting JacksonError.');
286
297
  } catch (err) {
@@ -293,16 +304,15 @@ tap.test('token()', (t) => {
293
304
  });
294
305
 
295
306
  t.test('Should throw an error if `code` is invalid', async (t) => {
296
- const body = {
307
+ const body: Partial<OAuthTokenReq> = {
297
308
  grant_type: 'authorization_code',
298
309
  client_id: `tenant=${samlConfig.tenant}&product=${samlConfig.product}`,
299
310
  client_secret: 'some-secret',
300
- redirect_uri: null,
301
311
  code: 'invalid-code',
302
312
  };
303
313
 
304
314
  try {
305
- await oauthController.token(body);
315
+ await oauthController.token(<OAuthTokenReq>body);
306
316
 
307
317
  t.fail('Expecting JacksonError.');
308
318
  } catch (err) {
@@ -315,11 +325,10 @@ tap.test('token()', (t) => {
315
325
  });
316
326
 
317
327
  t.test('Should return the `access_token` for a valid request', async (t) => {
318
- const body = {
328
+ const body: Partial<OAuthTokenReq> = {
319
329
  grant_type: 'authorization_code',
320
330
  client_id: `tenant=${samlConfig.tenant}&product=${samlConfig.product}`,
321
331
  client_secret: 'some-secret',
322
- redirect_uri: null,
323
332
  code: code,
324
333
  };
325
334
 
@@ -329,7 +338,7 @@ tap.test('token()', (t) => {
329
338
  //@ts-ignore
330
339
  .returns(token);
331
340
 
332
- const response = await oauthController.token(body);
341
+ const response = await oauthController.token(<OAuthTokenReq>body);
333
342
 
334
343
  t.ok(stubRandomBytes.calledOnce, 'randomBytes called once');
335
344
  t.ok('access_token' in response, 'includes access_token');
package/src/typings.ts CHANGED
@@ -51,7 +51,7 @@ declare module 'saml-jackson' {
51
51
  authorize(body: OAuthReqBody): Promise<{ redirect_url: string }>;
52
52
  samlResponse(body: SAMLResponsePayload): Promise<{ redirect_url: string }>;
53
53
  token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
54
- userInfo(body: string): Promise<Profile>;
54
+ userInfo(token: string): Promise<Profile>;
55
55
  }
56
56
 
57
57
  export interface OAuthReqBody {
@@ -118,8 +118,8 @@ declare module 'saml-jackson' {
118
118
  }
119
119
 
120
120
  export interface Encrypted {
121
- iv: string;
122
- tag: string;
121
+ iv?: string;
122
+ tag?: string;
123
123
  value: string;
124
124
  }
125
125