@boxyhq/saml-jackson 0.2.3-beta.231 → 0.2.3-beta.235

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. package/README.md +1 -2
  2. package/package.json +12 -4
  3. package/ nodemon.json +0 -12
  4. package/.dockerignore +0 -2
  5. package/.eslintrc.js +0 -18
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
  7. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -43
  9. package/.github/pull_request_template.md +0 -31
  10. package/.github/workflows/codesee-arch-diagram.yml +0 -81
  11. package/.github/workflows/main.yml +0 -123
  12. package/_dev/docker-compose.yml +0 -37
  13. package/map.js +0 -1
  14. package/prettier.config.js +0 -4
  15. package/src/controller/api.ts +0 -225
  16. package/src/controller/error.ts +0 -13
  17. package/src/controller/oauth/allowed.ts +0 -22
  18. package/src/controller/oauth/code-verifier.ts +0 -11
  19. package/src/controller/oauth/redirect.ts +0 -12
  20. package/src/controller/oauth.ts +0 -334
  21. package/src/controller/utils.ts +0 -17
  22. package/src/db/db.ts +0 -100
  23. package/src/db/encrypter.ts +0 -38
  24. package/src/db/mem.ts +0 -128
  25. package/src/db/mongo.ts +0 -110
  26. package/src/db/redis.ts +0 -103
  27. package/src/db/sql/entity/JacksonIndex.ts +0 -43
  28. package/src/db/sql/entity/JacksonStore.ts +0 -43
  29. package/src/db/sql/entity/JacksonTTL.ts +0 -17
  30. package/src/db/sql/model/JacksonIndex.ts +0 -3
  31. package/src/db/sql/model/JacksonStore.ts +0 -8
  32. package/src/db/sql/sql.ts +0 -181
  33. package/src/db/store.ts +0 -49
  34. package/src/db/utils.ts +0 -26
  35. package/src/env.ts +0 -42
  36. package/src/index.ts +0 -84
  37. package/src/jackson.ts +0 -173
  38. package/src/read-config.ts +0 -29
  39. package/src/saml/claims.ts +0 -41
  40. package/src/saml/saml.ts +0 -233
  41. package/src/saml/x509.ts +0 -51
  42. package/src/test/api.test.ts +0 -270
  43. package/src/test/data/metadata/boxyhq.js +0 -6
  44. package/src/test/data/metadata/boxyhq.xml +0 -30
  45. package/src/test/data/saml_response +0 -1
  46. package/src/test/db.test.ts +0 -313
  47. package/src/test/oauth.test.ts +0 -362
  48. package/src/typings.ts +0 -167
  49. package/tsconfig.build.json +0 -6
  50. package/tsconfig.json +0 -26
package/src/db/mongo.ts DELETED
@@ -1,110 +0,0 @@
1
- import { MongoClient } from 'mongodb';
2
- import { DatabaseDriver, DatabaseOption, Encrypted, Index } from 'saml-jackson';
3
- import * as dbutils from './utils';
4
-
5
- type Document = {
6
- value: Encrypted;
7
- expiresAt: Date;
8
- indexes: string[];
9
- };
10
-
11
- class Mongo implements DatabaseDriver {
12
- private options: DatabaseOption;
13
- private client!: MongoClient;
14
- private collection: any;
15
- private db: any;
16
-
17
- constructor(options: DatabaseOption) {
18
- this.options = options;
19
- }
20
-
21
- async init(): Promise<Mongo> {
22
- this.client = new MongoClient(this.options.url);
23
-
24
- await this.client.connect();
25
-
26
- this.db = this.client.db();
27
- this.collection = this.db.collection('jacksonStore');
28
-
29
- await this.collection.createIndex({ indexes: 1 });
30
- await this.collection.createIndex(
31
- { expiresAt: 1 },
32
- { expireAfterSeconds: 1 }
33
- );
34
-
35
- return this;
36
- }
37
-
38
- async get(namespace: string, key: string): Promise<any> {
39
- const res = await this.collection.findOne({
40
- _id: dbutils.key(namespace, key),
41
- });
42
- if (res && res.value) {
43
- return res.value;
44
- }
45
-
46
- return null;
47
- }
48
-
49
- async getByIndex(namespace: string, idx: Index): Promise<any> {
50
- const docs = await this.collection
51
- .find({
52
- indexes: dbutils.keyForIndex(namespace, idx),
53
- })
54
- .toArray();
55
-
56
- const ret: string[] = [];
57
- for (const doc of docs || []) {
58
- ret.push(doc.value);
59
- }
60
-
61
- return ret;
62
- }
63
-
64
- async put(
65
- namespace: string,
66
- key: string,
67
- val: Encrypted,
68
- ttl = 0,
69
- ...indexes: any[]
70
- ): Promise<void> {
71
- const doc = <Document>{
72
- value: val,
73
- };
74
-
75
- if (ttl) {
76
- doc.expiresAt = new Date(Date.now() + ttl * 1000);
77
- }
78
-
79
- // no ttl support for secondary indexes
80
- for (const idx of indexes || []) {
81
- const idxKey = dbutils.keyForIndex(namespace, idx);
82
-
83
- if (!doc.indexes) {
84
- doc.indexes = [];
85
- }
86
-
87
- doc.indexes.push(idxKey);
88
- }
89
-
90
- await this.collection.updateOne(
91
- { _id: dbutils.key(namespace, key) },
92
- {
93
- $set: doc,
94
- },
95
- { upsert: true }
96
- );
97
- }
98
-
99
- async delete(namespace: string, key: string): Promise<any> {
100
- return await this.collection.deleteOne({
101
- _id: dbutils.key(namespace, key),
102
- });
103
- }
104
- }
105
-
106
- export default {
107
- new: async (options: DatabaseOption): Promise<Mongo> => {
108
- return await new Mongo(options).init();
109
- },
110
- };
package/src/db/redis.ts DELETED
@@ -1,103 +0,0 @@
1
- import * as redis from 'redis';
2
- import { DatabaseDriver, DatabaseOption, Encrypted, Index } from 'saml-jackson';
3
- import * as dbutils from './utils';
4
-
5
- class Redis implements DatabaseDriver {
6
- private options: DatabaseOption;
7
- private client!: any;
8
-
9
- constructor(options: DatabaseOption) {
10
- this.options = options;
11
- }
12
-
13
- async init(): Promise<Redis> {
14
- const opts = {};
15
-
16
- if (this.options && this.options.url) {
17
- opts['socket'] = {
18
- url: this.options.url,
19
- };
20
- }
21
-
22
- this.client = redis.createClient(opts);
23
- this.client.on('error', (err: any) =>
24
- console.log('Redis Client Error', err)
25
- );
26
-
27
- await this.client.connect();
28
-
29
- return this;
30
- }
31
-
32
- async get(namespace: string, key: string): Promise<any> {
33
- const res = await this.client.get(dbutils.key(namespace, key));
34
- if (res) {
35
- return JSON.parse(res);
36
- }
37
-
38
- return null;
39
- }
40
-
41
- async getByIndex(namespace: string, idx: Index): Promise<any> {
42
- const dbKeys = await this.client.sMembers(
43
- dbutils.keyForIndex(namespace, idx)
44
- );
45
-
46
- const ret: string[] = [];
47
- for (const dbKey of dbKeys || []) {
48
- ret.push(await this.get(namespace, dbKey));
49
- }
50
-
51
- return ret;
52
- }
53
-
54
- async put(
55
- namespace: string,
56
- key: string,
57
- val: Encrypted,
58
- ttl = 0,
59
- ...indexes: any[]
60
- ): Promise<void> {
61
- let tx = this.client.multi();
62
- const k = dbutils.key(namespace, key);
63
-
64
- tx = tx.set(k, JSON.stringify(val));
65
-
66
- if (ttl) {
67
- tx = tx.expire(k, ttl);
68
- }
69
-
70
- // no ttl support for secondary indexes
71
- for (const idx of indexes || []) {
72
- const idxKey = dbutils.keyForIndex(namespace, idx);
73
- tx = tx.sAdd(idxKey, key);
74
- tx = tx.sAdd(dbutils.keyFromParts(dbutils.indexPrefix, k), idxKey);
75
- }
76
-
77
- await tx.exec();
78
- }
79
-
80
- async delete(namespace: string, key: string): Promise<any> {
81
- let tx = this.client.multi();
82
- const k = dbutils.key(namespace, key);
83
- tx = tx.del(k);
84
-
85
- const idxKey = dbutils.keyFromParts(dbutils.indexPrefix, k);
86
- // delete secondary indexes and then the mapping of the seconary indexes
87
- const dbKeys = await this.client.sMembers(idxKey);
88
-
89
- for (const dbKey of dbKeys || []) {
90
- tx.sRem(dbKey, key);
91
- }
92
-
93
- tx.del(idxKey);
94
-
95
- return await tx.exec();
96
- }
97
- }
98
-
99
- export default {
100
- new: async (options: DatabaseOption): Promise<Redis> => {
101
- return await new Redis(options).init();
102
- },
103
- };
@@ -1,43 +0,0 @@
1
- import { EntitySchema } from 'typeorm';
2
- import { JacksonIndex } from '../model/JacksonIndex';
3
- import { JacksonStore } from '../model/JacksonStore';
4
-
5
- export default new EntitySchema({
6
- name: 'JacksonIndex',
7
- target: JacksonIndex,
8
- columns: {
9
- id: {
10
- primary: true,
11
- generated: true,
12
- type: 'int',
13
- },
14
- key: {
15
- type: 'varchar',
16
- length: 1500,
17
- },
18
- storeKey: {
19
- type: 'varchar',
20
- length: 1500,
21
- },
22
- },
23
- relations: {
24
- // @ts-ignore
25
- store: {
26
- target: () => JacksonStore,
27
- type: 'many-to-one',
28
- inverseSide: 'indexes',
29
- eager: true,
30
- onDelete: 'CASCADE',
31
- },
32
- },
33
- indices: [
34
- {
35
- name: '_jackson_index_key',
36
- columns: ['key'],
37
- },
38
- {
39
- name: '_jackson_index_key_store',
40
- columns: ['key', 'storeKey'],
41
- },
42
- ],
43
- });
@@ -1,43 +0,0 @@
1
- import { DatabaseType } from 'saml-jackson';
2
- import { ColumnType, EntitySchema } from 'typeorm';
3
- import { JacksonStore } from '../model/JacksonStore';
4
-
5
- const valueType = (type: DatabaseType): ColumnType => {
6
- switch (type) {
7
- case 'postgres':
8
- case 'cockroachdb':
9
- return 'text';
10
- case 'mysql':
11
- case 'mariadb':
12
- return 'mediumtext';
13
- default:
14
- return 'varchar';
15
- }
16
- };
17
-
18
- export default (type: DatabaseType) => {
19
- return new EntitySchema({
20
- name: 'JacksonStore',
21
- target: JacksonStore,
22
- columns: {
23
- key: {
24
- primary: true,
25
- type: 'varchar',
26
- length: 1500,
27
- },
28
- value: {
29
- type: valueType(type),
30
- },
31
- iv: {
32
- type: 'varchar',
33
- length: 64,
34
- nullable: true,
35
- },
36
- tag: {
37
- type: 'varchar',
38
- length: 64,
39
- nullable: true,
40
- },
41
- },
42
- });
43
- };
@@ -1,17 +0,0 @@
1
- import { Entity, Column, Index } from 'typeorm';
2
-
3
- @Entity()
4
- export class JacksonTTL {
5
- @Column({
6
- primary: true,
7
- type: 'varchar',
8
- length: 1500,
9
- })
10
- key!: string;
11
-
12
- @Index('_jackson_ttl_expires_at')
13
- @Column({
14
- type: 'bigint',
15
- })
16
- expiresAt!: number;
17
- }
@@ -1,3 +0,0 @@
1
- export class JacksonIndex {
2
- constructor(public id: number, public key: string, public store: any) {}
3
- }
@@ -1,8 +0,0 @@
1
- export class JacksonStore {
2
- constructor(
3
- public key: string,
4
- public value: string,
5
- public iv?: string,
6
- public tag?: string
7
- ) {}
8
- }
package/src/db/sql/sql.ts DELETED
@@ -1,181 +0,0 @@
1
- /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
2
-
3
- require('reflect-metadata');
4
-
5
- import { DatabaseDriver, DatabaseOption, Index, Encrypted } from 'saml-jackson';
6
- import { Connection, createConnection } from 'typeorm';
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';
14
- import { JacksonIndex } from './model/JacksonIndex';
15
- import { JacksonStore } from './model/JacksonStore';
16
-
17
- class Sql implements DatabaseDriver {
18
- private options: DatabaseOption;
19
- private connection!: Connection;
20
- private storeRepository;
21
- private indexRepository;
22
- private ttlRepository;
23
- private ttlCleanup;
24
- private timerId;
25
-
26
- constructor(options: DatabaseOption) {
27
- this.options = options;
28
- }
29
-
30
- async init(): Promise<Sql> {
31
- while (true) {
32
- try {
33
- this.connection = await createConnection({
34
- name: this.options.type + Math.floor(Math.random() * 100000),
35
- type: this.options.type,
36
- url: this.options.url,
37
- synchronize: true,
38
- migrationsTableName: '_jackson_migrations',
39
- logging: false,
40
- entities: [
41
- JacksonStoreEntity(this.options.type),
42
- JacksonIndexEntity,
43
- JacksonTTLEntity,
44
- ],
45
- });
46
-
47
- break;
48
- } catch (err) {
49
- console.error(`error connecting to ${this.options.type} db: ${err}`);
50
- await dbutils.sleep(1000);
51
- continue;
52
- }
53
- }
54
-
55
- this.storeRepository = this.connection.getRepository(JacksonStore);
56
- this.indexRepository = this.connection.getRepository(JacksonIndex);
57
- this.ttlRepository = this.connection.getRepository(JacksonTTL);
58
-
59
- if (this.options.ttl && this.options.cleanupLimit) {
60
- this.ttlCleanup = async () => {
61
- const now = Date.now();
62
-
63
- while (true) {
64
- const ids = await this.ttlRepository
65
- .createQueryBuilder('jackson_ttl')
66
- .limit(this.options.cleanupLimit)
67
- .where('jackson_ttl.expiresAt <= :expiresAt', {
68
- expiresAt: now,
69
- })
70
- .getMany();
71
-
72
- if (ids.length <= 0) {
73
- break;
74
- }
75
-
76
- const delIds = ids.map((id) => {
77
- return id.key;
78
- });
79
-
80
- await this.storeRepository.remove(ids);
81
- await this.ttlRepository.delete(delIds);
82
- }
83
-
84
- this.timerId = setTimeout(this.ttlCleanup, this.options.ttl * 1000);
85
- };
86
-
87
- this.timerId = setTimeout(this.ttlCleanup, this.options.ttl * 1000);
88
- } else {
89
- console.log(
90
- 'Warning: ttl cleanup not enabled, set both "ttl" and "cleanupLimit" options to enable it!'
91
- );
92
- }
93
-
94
- return this;
95
- }
96
-
97
- async get(namespace: string, key: string): Promise<any> {
98
- let res = await this.storeRepository.findOne({
99
- key: dbutils.key(namespace, key),
100
- });
101
-
102
- if (res && res.value) {
103
- return {
104
- value: res.value,
105
- iv: res.iv,
106
- tag: res.tag,
107
- };
108
- }
109
-
110
- return null;
111
- }
112
-
113
- async getByIndex(namespace: string, idx: Index): Promise<any> {
114
- const res = await this.indexRepository.find({
115
- key: dbutils.keyForIndex(namespace, idx),
116
- });
117
-
118
- const ret: Encrypted[] = [];
119
-
120
- if (res) {
121
- res.forEach((r) => {
122
- ret.push({
123
- value: r.store.value,
124
- iv: r.store.iv,
125
- tag: r.store.tag,
126
- });
127
- });
128
- }
129
-
130
- return ret;
131
- }
132
-
133
- async put(
134
- namespace: string,
135
- key: string,
136
- val: Encrypted,
137
- ttl: number = 0,
138
- ...indexes: any[]
139
- ): Promise<void> {
140
- await this.connection.transaction(async (transactionalEntityManager) => {
141
- const dbKey = dbutils.key(namespace, key);
142
-
143
- const store = new JacksonStore(dbKey, val.value, val.iv, val.tag);
144
-
145
- await transactionalEntityManager.save(store);
146
-
147
- if (ttl) {
148
- const ttlRec = new JacksonTTL();
149
- ttlRec.key = dbKey;
150
- ttlRec.expiresAt = Date.now() + ttl * 1000;
151
- await transactionalEntityManager.save(ttlRec);
152
- }
153
-
154
- // no ttl support for secondary indexes
155
- for (const idx of indexes || []) {
156
- const key = dbutils.keyForIndex(namespace, idx);
157
- const rec = await this.indexRepository.findOne({
158
- key,
159
- storeKey: store.key,
160
- });
161
- if (!rec) {
162
- await transactionalEntityManager.save(
163
- new JacksonIndex(0, key, store)
164
- );
165
- }
166
- }
167
- });
168
- }
169
-
170
- async delete(namespace: string, key: string): Promise<any> {
171
- return await this.storeRepository.remove({
172
- key: dbutils.key(namespace, key),
173
- });
174
- }
175
- }
176
-
177
- export default {
178
- new: async (options: DatabaseOption): Promise<Sql> => {
179
- return await new Sql(options).init();
180
- },
181
- };
package/src/db/store.ts DELETED
@@ -1,49 +0,0 @@
1
- import { Index, Storable } from 'saml-jackson';
2
- import * as dbutils from './utils';
3
-
4
- class Store implements Storable {
5
- private namespace: string;
6
- private db: any;
7
- private ttl: number;
8
-
9
- constructor(namespace: string, db: any, ttl = 0) {
10
- this.namespace = namespace;
11
- this.db = db;
12
- this.ttl = ttl;
13
- }
14
-
15
- async get(key: string): Promise<any> {
16
- return await this.db.get(this.namespace, dbutils.keyDigest(key));
17
- }
18
-
19
- async getByIndex(idx: Index): Promise<any> {
20
- idx.value = dbutils.keyDigest(idx.value);
21
-
22
- return await this.db.getByIndex(this.namespace, idx);
23
- }
24
-
25
- async put(key: string, val: any, ...indexes: Index[]): Promise<any> {
26
- indexes = (indexes || []).map((idx) => {
27
- idx.value = dbutils.keyDigest(idx.value);
28
- return idx;
29
- });
30
-
31
- return await this.db.put(
32
- this.namespace,
33
- dbutils.keyDigest(key),
34
- val,
35
- this.ttl,
36
- ...indexes
37
- );
38
- }
39
-
40
- async delete(key: string): Promise<any> {
41
- return await this.db.delete(this.namespace, dbutils.keyDigest(key));
42
- }
43
- }
44
-
45
- export default {
46
- new: (namespace: string, db: any, ttl = 0): Storable => {
47
- return new Store(namespace, db, ttl);
48
- },
49
- };
package/src/db/utils.ts DELETED
@@ -1,26 +0,0 @@
1
- import Ripemd160 from 'ripemd160';
2
- import { Index } from 'saml-jackson';
3
-
4
- export const key = (namespace: string, k: string): string => {
5
- return namespace + ':' + k;
6
- };
7
-
8
- export const keyForIndex = (namespace: string, idx: Index): string => {
9
- return key(key(namespace, idx.name), idx.value);
10
- };
11
-
12
- export const keyDigest = (k: string): string => {
13
- return new Ripemd160().update(k).digest('hex');
14
- };
15
-
16
- export const keyFromParts = (...parts: string[]): string => {
17
- // TODO: pick a better strategy, keys can collide now
18
-
19
- return parts.join(':');
20
- };
21
-
22
- export const sleep = (ms: number): Promise<void> => {
23
- return new Promise((resolve) => setTimeout(resolve, ms));
24
- };
25
-
26
- export const indexPrefix = '_index';
package/src/env.ts DELETED
@@ -1,42 +0,0 @@
1
- const hostUrl = process.env.HOST_URL || 'localhost';
2
- const hostPort = +(process.env.HOST_PORT || '5000');
3
- const externalUrl =
4
- process.env.EXTERNAL_URL || 'http://' + hostUrl + ':' + hostPort;
5
- const samlPath = process.env.SAML_PATH || '/oauth/saml';
6
-
7
- const internalHostUrl = process.env.INTERNAL_HOST_URL || 'localhost';
8
- const internalHostPort = +(process.env.INTERNAL_HOST_PORT || '6000');
9
-
10
- const apiKeys = (process.env.JACKSON_API_KEYS || '').split(',');
11
-
12
- const samlAudience = process.env.SAML_AUDIENCE || 'https://saml.boxyhq.com';
13
- const preLoadedConfig = process.env.PRE_LOADED_CONFIG;
14
-
15
- const idpEnabled = process.env.IDP_ENABLED;
16
-
17
- const db = {
18
- engine: process.env.DB_ENGINE,
19
- url: process.env.DB_URL,
20
- type: process.env.DB_TYPE,
21
- ttl: process.env.DB_TTL,
22
- encryptionKey: process.env.DB_ENCRYPTION_KEY,
23
- };
24
-
25
- const env = {
26
- hostUrl,
27
- hostPort,
28
- externalUrl,
29
- samlPath,
30
- samlAudience,
31
- preLoadedConfig,
32
- internalHostUrl,
33
- internalHostPort,
34
- apiKeys,
35
- idpEnabled,
36
- db,
37
- useInternalServer: !(
38
- hostUrl === internalHostUrl && hostPort === internalHostPort
39
- ),
40
- };
41
-
42
- export default env;