@boxyhq/saml-jackson 0.2.3-beta.224 → 0.2.3-beta.228

Sign up to get free protection for your applications and to get access to all the features.
package/.eslintrc.js CHANGED
@@ -1,13 +1,18 @@
1
1
  module.exports = {
2
- "env": {
3
- "es2021": true,
4
- "node": true
5
- },
6
- "extends": "eslint:recommended",
7
- "parserOptions": {
8
- "ecmaVersion": 13,
9
- "sourceType": "module"
10
- },
11
- "rules": {
12
- }
2
+ env: {
3
+ es2021: true,
4
+ node: true,
5
+ },
6
+ parserOptions: {
7
+ ecmaVersion: 13,
8
+ sourceType: 'module',
9
+ },
10
+ root: true,
11
+ parser: '@typescript-eslint/parser',
12
+ plugins: ['@typescript-eslint'],
13
+ extends: [
14
+ 'eslint:recommended',
15
+ 'plugin:@typescript-eslint/recommended',
16
+ 'prettier',
17
+ ],
13
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxyhq/saml-jackson",
3
- "version": "0.2.3-beta.224",
3
+ "version": "0.2.3-beta.228",
4
4
  "license": "Apache 2.0",
5
5
  "description": "SAML 2.0 service",
6
6
  "main": "dist/index.js",
@@ -57,8 +57,11 @@
57
57
  "@types/redis": "4.0.11",
58
58
  "@types/sinon": "10.0.6",
59
59
  "@types/tap": "15.0.5",
60
+ "@typescript-eslint/eslint-plugin": "^5.8.1",
61
+ "@typescript-eslint/parser": "^5.8.1",
60
62
  "cross-env": "7.0.3",
61
- "eslint": "8.5.0",
63
+ "eslint": "^8.5.0",
64
+ "eslint-config-prettier": "^8.3.0",
62
65
  "husky": "7.0.4",
63
66
  "lint-staged": "12.1.4",
64
67
  "nodemon": "2.0.15",
@@ -66,7 +69,7 @@
66
69
  "sinon": "12.0.1",
67
70
  "tap": "15.1.5",
68
71
  "ts-node": "10.4.0",
69
- "typescript": "4.5.4"
72
+ "typescript": "^4.5.4"
70
73
  },
71
74
  "lint-staged": {
72
75
  "*.{js,ts}": "eslint --cache --fix",
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