@boxyhq/saml-jackson 1.2.2 → 1.3.0
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/dist/controller/admin.d.ts +4 -4
- package/dist/controller/admin.js +6 -6
- package/dist/controller/api.d.ts +448 -204
- package/dist/controller/api.js +547 -378
- package/dist/controller/connection/oidc.d.ts +18 -0
- package/dist/controller/connection/oidc.js +145 -0
- package/dist/controller/connection/saml.d.ts +14 -0
- package/dist/controller/connection/saml.js +168 -0
- package/dist/controller/logout.d.ts +3 -3
- package/dist/controller/logout.js +14 -14
- package/dist/controller/oauth.d.ts +26 -8
- package/dist/controller/oauth.js +361 -140
- package/dist/controller/utils.d.ts +10 -2
- package/dist/controller/utils.js +88 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +26 -14
- package/dist/loadConnection.d.ts +3 -0
- package/dist/{read-config.js → loadConnection.js} +13 -12
- package/dist/opentelemetry/metrics.js +12 -12
- package/dist/typings.d.ts +109 -35
- package/package.json +3 -2
- package/dist/read-config.d.ts +0 -3
package/dist/controller/api.js
CHANGED
@@ -31,394 +31,531 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
31
31
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
32
32
|
});
|
33
33
|
};
|
34
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
35
|
-
var t = {};
|
36
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
37
|
-
t[p] = s[p];
|
38
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
39
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
40
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
41
|
-
t[p[i]] = s[p[i]];
|
42
|
-
}
|
43
|
-
return t;
|
44
|
-
};
|
45
34
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
46
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
47
36
|
};
|
48
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
49
|
-
exports.
|
50
|
-
const crypto_1 = __importDefault(require("crypto"));
|
38
|
+
exports.ConnectionAPIController = void 0;
|
51
39
|
const dbutils = __importStar(require("../db/utils"));
|
52
40
|
const metrics = __importStar(require("../opentelemetry/metrics"));
|
53
|
-
const saml20_1 = __importDefault(require("@boxyhq/saml20"));
|
54
|
-
const x509_1 = __importDefault(require("../saml/x509"));
|
55
41
|
const error_1 = require("./error");
|
56
42
|
const utils_1 = require("./utils");
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
}
|
61
|
-
|
62
|
-
if (redirectUrlList) {
|
63
|
-
if (redirectUrlList.length > 100) {
|
64
|
-
throw new error_1.JacksonError('Exceeded maximum number of allowed redirect urls', 400);
|
65
|
-
}
|
66
|
-
for (const url of redirectUrlList) {
|
67
|
-
(0, utils_1.validateAbsoluteUrl)(url, 'redirectUrl is invalid');
|
68
|
-
}
|
69
|
-
}
|
70
|
-
if (defaultRedirectUrl) {
|
71
|
-
(0, utils_1.validateAbsoluteUrl)(defaultRedirectUrl, 'defaultRedirectUrl is invalid');
|
72
|
-
}
|
73
|
-
}
|
74
|
-
_validateIdPConfig(body) {
|
75
|
-
const { encodedRawMetadata, rawMetadata, defaultRedirectUrl, redirectUrl, tenant, product, description } = body;
|
76
|
-
if (!rawMetadata && !encodedRawMetadata) {
|
77
|
-
throw new error_1.JacksonError('Please provide rawMetadata or encodedRawMetadata', 400);
|
78
|
-
}
|
79
|
-
if (!defaultRedirectUrl) {
|
80
|
-
throw new error_1.JacksonError('Please provide a defaultRedirectUrl', 400);
|
81
|
-
}
|
82
|
-
if (!redirectUrl) {
|
83
|
-
throw new error_1.JacksonError('Please provide redirectUrl', 400);
|
84
|
-
}
|
85
|
-
if (!tenant) {
|
86
|
-
throw new error_1.JacksonError('Please provide tenant', 400);
|
87
|
-
}
|
88
|
-
if (!product) {
|
89
|
-
throw new error_1.JacksonError('Please provide product', 400);
|
90
|
-
}
|
91
|
-
if (description && description.length > 100) {
|
92
|
-
throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
|
93
|
-
}
|
43
|
+
const oidc_1 = __importDefault(require("./connection/oidc"));
|
44
|
+
const saml_1 = __importDefault(require("./connection/saml"));
|
45
|
+
class ConnectionAPIController {
|
46
|
+
constructor({ connectionStore }) {
|
47
|
+
this.connectionStore = connectionStore;
|
94
48
|
}
|
95
49
|
/**
|
96
50
|
* @swagger
|
51
|
+
* definitions:
|
52
|
+
* Connection:
|
53
|
+
* type: object
|
54
|
+
* example:
|
55
|
+
* {
|
56
|
+
* "idpMetadata": {
|
57
|
+
* "sso": {
|
58
|
+
* "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxsso/saml",
|
59
|
+
* "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxsso/saml"
|
60
|
+
* },
|
61
|
+
* "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
|
62
|
+
* "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
|
63
|
+
* "loginType": "idp",
|
64
|
+
* "provider": "okta.com"
|
65
|
+
* },
|
66
|
+
* "defaultRedirectUrl": "https://hoppscotch.io/",
|
67
|
+
* "redirectUrl": ["https://hoppscotch.io/"],
|
68
|
+
* "tenant": "hoppscotch.io",
|
69
|
+
* "product": "API Engine",
|
70
|
+
* "name": "Hoppscotch-SP",
|
71
|
+
* "description": "SP for hoppscotch.io",
|
72
|
+
* "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
|
73
|
+
* "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
|
74
|
+
* "certs": {
|
75
|
+
* "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
|
76
|
+
* "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
|
77
|
+
* }
|
78
|
+
* }
|
79
|
+
* validationErrorsPost:
|
80
|
+
* description: Please provide rawMetadata or encodedRawMetadata | Please provide a defaultRedirectUrl | Please provide redirectUrl | redirectUrl is invalid | Exceeded maximum number of allowed redirect urls | defaultRedirectUrl is invalid | Please provide tenant | Please provide product | Please provide a friendly name | Description should not exceed 100 characters | Strategy: xxxx not supported | Please provide the clientId from OpenID Provider | Please provide the clientSecret from OpenID Provider | Please provide the discoveryUrl for the OpenID Provider
|
97
81
|
*
|
82
|
+
* parameters:
|
83
|
+
* nameParamPost:
|
84
|
+
* name: name
|
85
|
+
* description: Name/identifier for the connection
|
86
|
+
* type: string
|
87
|
+
* in: formData
|
88
|
+
* descriptionParamPost:
|
89
|
+
* name: description
|
90
|
+
* description: A short description for the connection not more than 100 characters
|
91
|
+
* type: string
|
92
|
+
* in: formData
|
93
|
+
* encodedRawMetadataParamPost:
|
94
|
+
* name: encodedRawMetadata
|
95
|
+
* description: Base64 encoding of the XML metadata
|
96
|
+
* in: formData
|
97
|
+
* type: string
|
98
|
+
* rawMetadataParamPost:
|
99
|
+
* name: rawMetadata
|
100
|
+
* description: Raw XML metadata
|
101
|
+
* in: formData
|
102
|
+
* type: string
|
103
|
+
* defaultRedirectUrlParamPost:
|
104
|
+
* name: defaultRedirectUrl
|
105
|
+
* description: The redirect URL to use in the IdP login flow
|
106
|
+
* in: formData
|
107
|
+
* required: true
|
108
|
+
* type: string
|
109
|
+
* redirectUrlParamPost:
|
110
|
+
* name: redirectUrl
|
111
|
+
* description: JSON encoded array containing a list of allowed redirect URLs
|
112
|
+
* in: formData
|
113
|
+
* required: true
|
114
|
+
* type: string
|
115
|
+
* tenantParamPost:
|
116
|
+
* name: tenant
|
117
|
+
* description: Tenant
|
118
|
+
* in: formData
|
119
|
+
* required: true
|
120
|
+
* type: string
|
121
|
+
* productParamPost:
|
122
|
+
* name: product
|
123
|
+
* description: Product
|
124
|
+
* in: formData
|
125
|
+
* required: true
|
126
|
+
* type: string
|
127
|
+
* oidcDiscoveryUrlPost:
|
128
|
+
* name: oidcDiscoveryUrl
|
129
|
+
* description: well-known URL where the OpenID Provider configuration is exposed
|
130
|
+
* in: formData
|
131
|
+
* type: string
|
132
|
+
* oidcClientIdPost:
|
133
|
+
* name: oidcClientId
|
134
|
+
* description: clientId of the application set up on the OpenID Provider
|
135
|
+
* in: formData
|
136
|
+
* type: string
|
137
|
+
* oidcClientSecretPost:
|
138
|
+
* name: oidcClientSecret
|
139
|
+
* description: clientSecret of the application set up on the OpenID Provider
|
140
|
+
* in: formData
|
141
|
+
* type: string
|
98
142
|
* /api/v1/saml/config:
|
99
143
|
* post:
|
100
|
-
*
|
101
|
-
*
|
102
|
-
*
|
144
|
+
* summary: Create SAML config
|
145
|
+
* operationId: create-saml-config
|
146
|
+
* deprecated: true
|
147
|
+
* tags: [SAML Config - Deprecated]
|
148
|
+
* produces:
|
149
|
+
* - application/json
|
150
|
+
* consumes:
|
151
|
+
* - application/x-www-form-urlencoded
|
152
|
+
* - application/json
|
153
|
+
* parameters:
|
154
|
+
* - $ref: '#/parameters/nameParamPost'
|
155
|
+
* - $ref: '#/parameters/descriptionParamPost'
|
156
|
+
* - $ref: '#/parameters/encodedRawMetadataParamPost'
|
157
|
+
* - $ref: '#/parameters/rawMetadataParamPost'
|
158
|
+
* - $ref: '#/parameters/defaultRedirectUrlParamPost'
|
159
|
+
* - $ref: '#/parameters/redirectUrlParamPost'
|
160
|
+
* - $ref: '#/parameters/tenantParamPost'
|
161
|
+
* - $ref: '#/parameters/productParamPost'
|
162
|
+
* responses:
|
163
|
+
* 200:
|
164
|
+
* description: Success
|
165
|
+
* schema:
|
166
|
+
* $ref: '#/definitions/Connection'
|
167
|
+
* 400:
|
168
|
+
* $ref: '#/definitions/validationErrorsPost'
|
169
|
+
* 401:
|
170
|
+
* description: Unauthorized
|
171
|
+
* /api/v1/connections:
|
172
|
+
* post:
|
173
|
+
* summary: Create SSO connection
|
174
|
+
* operationId: create-sso-connection
|
175
|
+
* tags: [Connections]
|
103
176
|
* produces:
|
104
|
-
*
|
177
|
+
* - application/json
|
105
178
|
* consumes:
|
106
|
-
*
|
179
|
+
* - application/x-www-form-urlencoded
|
180
|
+
* - application/json
|
107
181
|
* parameters:
|
108
|
-
*
|
109
|
-
*
|
110
|
-
*
|
111
|
-
*
|
112
|
-
*
|
113
|
-
*
|
114
|
-
*
|
115
|
-
*
|
116
|
-
*
|
117
|
-
*
|
118
|
-
*
|
119
|
-
* type: string
|
120
|
-
* - name: rawMetadata
|
121
|
-
* description: Raw XML metadata
|
122
|
-
* in: formData
|
123
|
-
* type: string
|
124
|
-
* - name: defaultRedirectUrl
|
125
|
-
* description: The redirect URL to use in the IdP login flow
|
126
|
-
* in: formData
|
127
|
-
* required: true
|
128
|
-
* type: string
|
129
|
-
* - name: redirectUrl
|
130
|
-
* description: JSON encoded array containing a list of allowed redirect URLs
|
131
|
-
* in: formData
|
132
|
-
* required: true
|
133
|
-
* type: string
|
134
|
-
* - name: tenant
|
135
|
-
* description: Tenant
|
136
|
-
* in: formData
|
137
|
-
* required: true
|
138
|
-
* type: string
|
139
|
-
* - name: product
|
140
|
-
* description: Product
|
141
|
-
* in: formData
|
142
|
-
* required: true
|
143
|
-
* type: string
|
182
|
+
* - $ref: '#/parameters/nameParamPost'
|
183
|
+
* - $ref: '#/parameters/descriptionParamPost'
|
184
|
+
* - $ref: '#/parameters/encodedRawMetadataParamPost'
|
185
|
+
* - $ref: '#/parameters/rawMetadataParamPost'
|
186
|
+
* - $ref: '#/parameters/defaultRedirectUrlParamPost'
|
187
|
+
* - $ref: '#/parameters/redirectUrlParamPost'
|
188
|
+
* - $ref: '#/parameters/tenantParamPost'
|
189
|
+
* - $ref: '#/parameters/productParamPost'
|
190
|
+
* - $ref: '#/parameters/oidcDiscoveryUrlPost'
|
191
|
+
* - $ref: '#/parameters/oidcClientIdPost'
|
192
|
+
* - $ref: '#/parameters/oidcClientSecretPost'
|
144
193
|
* responses:
|
145
194
|
* 200:
|
146
195
|
* description: Success
|
147
196
|
* schema:
|
148
|
-
*
|
149
|
-
* example:
|
150
|
-
* {
|
151
|
-
* "idpMetadata": {
|
152
|
-
* "sso": {
|
153
|
-
* "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
|
154
|
-
* "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
|
155
|
-
* },
|
156
|
-
* "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
|
157
|
-
* "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
|
158
|
-
* "loginType": "idp",
|
159
|
-
* "provider": "okta.com"
|
160
|
-
* },
|
161
|
-
* "defaultRedirectUrl": "https://hoppscotch.io/",
|
162
|
-
* "redirectUrl": ["https://hoppscotch.io/"],
|
163
|
-
* "tenant": "hoppscotch.io",
|
164
|
-
* "product": "API Engine",
|
165
|
-
* "name": "Hoppscotch-SP",
|
166
|
-
* "description": "SP for hoppscotch.io",
|
167
|
-
* "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
|
168
|
-
* "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
|
169
|
-
* "certs": {
|
170
|
-
* "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
|
171
|
-
* "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
|
172
|
-
* }
|
173
|
-
* }
|
197
|
+
* $ref: '#/definitions/Connection'
|
174
198
|
* 400:
|
175
|
-
*
|
199
|
+
* $ref: '#/definitions/validationErrorsPost'
|
176
200
|
* 401:
|
177
201
|
* description: Unauthorized
|
178
202
|
*/
|
179
|
-
|
203
|
+
createSAMLConnection(body) {
|
180
204
|
return __awaiter(this, void 0, void 0, function* () {
|
181
|
-
|
182
|
-
const
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
}
|
197
|
-
idpMetadata.provider = providerName ? providerName : 'Unknown';
|
198
|
-
const clientID = dbutils.keyDigest(dbutils.keyFromParts(tenant, product, idpMetadata.entityID));
|
199
|
-
let clientSecret;
|
200
|
-
const exists = yield this.configStore.get(clientID);
|
201
|
-
if (exists) {
|
202
|
-
clientSecret = exists.clientSecret;
|
203
|
-
}
|
204
|
-
else {
|
205
|
-
clientSecret = crypto_1.default.randomBytes(24).toString('hex');
|
206
|
-
}
|
207
|
-
const certs = yield x509_1.default.generate();
|
208
|
-
if (!certs) {
|
209
|
-
throw new Error('Error generating x509 certs');
|
210
|
-
}
|
211
|
-
const record = {
|
212
|
-
idpMetadata,
|
213
|
-
defaultRedirectUrl,
|
214
|
-
redirectUrl: redirectUrlList,
|
215
|
-
tenant,
|
216
|
-
product,
|
217
|
-
name,
|
218
|
-
description,
|
219
|
-
clientID,
|
220
|
-
clientSecret,
|
221
|
-
certs,
|
222
|
-
forceAuthn,
|
223
|
-
};
|
224
|
-
yield this.configStore.put(clientID, record, {
|
225
|
-
// secondary index on entityID
|
226
|
-
name: utils_1.IndexNames.EntityID,
|
227
|
-
value: idpMetadata.entityID,
|
228
|
-
}, {
|
229
|
-
// secondary index on tenant + product
|
230
|
-
name: utils_1.IndexNames.TenantProduct,
|
231
|
-
value: dbutils.keyFromParts(tenant, product),
|
232
|
-
});
|
205
|
+
metrics.increment('createConnection');
|
206
|
+
const record = yield saml_1.default.create(body, this.connectionStore);
|
207
|
+
return record;
|
208
|
+
});
|
209
|
+
}
|
210
|
+
// For backwards compatibility
|
211
|
+
config(...args) {
|
212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
213
|
+
return this.createSAMLConnection(...args);
|
214
|
+
});
|
215
|
+
}
|
216
|
+
createOIDCConnection(body) {
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
218
|
+
metrics.increment('createConnection');
|
219
|
+
const record = yield oidc_1.default.create(body, this.connectionStore);
|
233
220
|
return record;
|
234
221
|
});
|
235
222
|
}
|
236
223
|
/**
|
237
224
|
* @swagger
|
238
|
-
*
|
225
|
+
* definitions:
|
226
|
+
* validationErrorsPatch:
|
227
|
+
* description: Please provide clientID | Please provide clientSecret | clientSecret mismatch | Tenant/Product config mismatch with IdP metadata | Description should not exceed 100 characters| redirectUrl is invalid | Exceeded maximum number of allowed redirect urls | defaultRedirectUrl is invalid | Tenant/Product config mismatch with OIDC Provider metadata
|
228
|
+
* parameters:
|
229
|
+
* clientIDParamPatch:
|
230
|
+
* name: clientID
|
231
|
+
* description: Client ID for the connection
|
232
|
+
* type: string
|
233
|
+
* in: formData
|
234
|
+
* required: true
|
235
|
+
* clientSecretParamPatch:
|
236
|
+
* name: clientSecret
|
237
|
+
* description: Client Secret for the connection
|
238
|
+
* type: string
|
239
|
+
* in: formData
|
240
|
+
* required: true
|
241
|
+
* nameParamPatch:
|
242
|
+
* name: name
|
243
|
+
* description: Name/identifier for the connection
|
244
|
+
* type: string
|
245
|
+
* in: formData
|
246
|
+
* descriptionParamPatch:
|
247
|
+
* name: description
|
248
|
+
* description: A short description for the connection not more than 100 characters
|
249
|
+
* type: string
|
250
|
+
* in: formData
|
251
|
+
* encodedRawMetadataParamPatch:
|
252
|
+
* name: encodedRawMetadata
|
253
|
+
* description: Base64 encoding of the XML metadata
|
254
|
+
* in: formData
|
255
|
+
* type: string
|
256
|
+
* rawMetadataParamPatch:
|
257
|
+
* name: rawMetadata
|
258
|
+
* description: Raw XML metadata
|
259
|
+
* in: formData
|
260
|
+
* type: string
|
261
|
+
* oidcDiscoveryUrlPatch:
|
262
|
+
* name: oidcDiscoveryUrl
|
263
|
+
* description: well-known URL where the OpenID Provider configuration is exposed
|
264
|
+
* in: formData
|
265
|
+
* type: string
|
266
|
+
* oidcClientIdPatch:
|
267
|
+
* name: oidcClientId
|
268
|
+
* description: clientId of the application set up on the OpenID Provider
|
269
|
+
* in: formData
|
270
|
+
* type: string
|
271
|
+
* oidcClientSecretPatch:
|
272
|
+
* name: oidcClientSecret
|
273
|
+
* description: clientSecret of the application set up on the OpenID Provider
|
274
|
+
* in: formData
|
275
|
+
* type: string
|
276
|
+
* defaultRedirectUrlParamPatch:
|
277
|
+
* name: defaultRedirectUrl
|
278
|
+
* description: The redirect URL to use in the IdP login flow
|
279
|
+
* in: formData
|
280
|
+
* type: string
|
281
|
+
* redirectUrlParamPatch:
|
282
|
+
* name: redirectUrl
|
283
|
+
* description: JSON encoded array containing a list of allowed redirect URLs
|
284
|
+
* in: formData
|
285
|
+
* type: string
|
286
|
+
* tenantParamPatch:
|
287
|
+
* name: tenant
|
288
|
+
* description: Tenant
|
289
|
+
* in: formData
|
290
|
+
* required: true
|
291
|
+
* type: string
|
292
|
+
* productParamPatch:
|
293
|
+
* name: product
|
294
|
+
* description: Product
|
295
|
+
* in: formData
|
296
|
+
* required: true
|
297
|
+
* type: string
|
239
298
|
* /api/v1/saml/config:
|
240
299
|
* patch:
|
241
|
-
* summary: Update SAML
|
300
|
+
* summary: Update SAML Config
|
242
301
|
* operationId: update-saml-config
|
243
|
-
* tags: [SAML Config]
|
302
|
+
* tags: [SAML Config - Deprecated]
|
303
|
+
* deprecated: true
|
304
|
+
* consumes:
|
305
|
+
* - application/json
|
306
|
+
* - application/x-www-form-urlencoded
|
307
|
+
* parameters:
|
308
|
+
* - $ref: '#/parameters/clientIDParamPatch'
|
309
|
+
* - $ref: '#/parameters/clientSecretParamPatch'
|
310
|
+
* - $ref: '#/parameters/nameParamPatch'
|
311
|
+
* - $ref: '#/parameters/descriptionParamPatch'
|
312
|
+
* - $ref: '#/parameters/encodedRawMetadataParamPatch'
|
313
|
+
* - $ref: '#/parameters/rawMetadataParamPatch'
|
314
|
+
* - $ref: '#/parameters/defaultRedirectUrlParamPatch'
|
315
|
+
* - $ref: '#/parameters/redirectUrlParamPatch'
|
316
|
+
* - $ref: '#/parameters/tenantParamPatch'
|
317
|
+
* - $ref: '#/parameters/productParamPatch'
|
318
|
+
* responses:
|
319
|
+
* 204:
|
320
|
+
* description: Success
|
321
|
+
* 400:
|
322
|
+
* $ref: '#/definitions/validationErrorsPatch'
|
323
|
+
* 401:
|
324
|
+
* description: Unauthorized
|
325
|
+
* /api/v1/connections:
|
326
|
+
* patch:
|
327
|
+
* summary: Update SSO Connection
|
328
|
+
* operationId: update-sso-connection
|
329
|
+
* tags: [Connections]
|
244
330
|
* consumes:
|
245
331
|
* - application/json
|
246
332
|
* - application/x-www-form-urlencoded
|
247
333
|
* parameters:
|
248
|
-
* -
|
249
|
-
*
|
250
|
-
*
|
251
|
-
*
|
252
|
-
*
|
253
|
-
* -
|
254
|
-
*
|
255
|
-
*
|
256
|
-
*
|
257
|
-
*
|
258
|
-
* -
|
259
|
-
*
|
260
|
-
*
|
261
|
-
* in: formData
|
262
|
-
* - name: description
|
263
|
-
* description: A short description for the config not more than 100 characters
|
264
|
-
* type: string
|
265
|
-
* in: formData
|
266
|
-
* - name: encodedRawMetadata
|
267
|
-
* description: Base64 encoding of the XML metadata
|
268
|
-
* in: formData
|
269
|
-
* type: string
|
270
|
-
* - name: rawMetadata
|
271
|
-
* description: Raw XML metadata
|
272
|
-
* in: formData
|
273
|
-
* type: string
|
274
|
-
* - name: defaultRedirectUrl
|
275
|
-
* description: The redirect URL to use in the IdP login flow
|
276
|
-
* in: formData
|
277
|
-
* required: true
|
278
|
-
* type: string
|
279
|
-
* - name: redirectUrl
|
280
|
-
* description: JSON encoded array containing a list of allowed redirect URLs
|
281
|
-
* in: formData
|
282
|
-
* required: true
|
283
|
-
* type: string
|
284
|
-
* - name: tenant
|
285
|
-
* description: Tenant
|
286
|
-
* in: formData
|
287
|
-
* required: true
|
288
|
-
* type: string
|
289
|
-
* - name: product
|
290
|
-
* description: Product
|
291
|
-
* in: formData
|
292
|
-
* required: true
|
293
|
-
* type: string
|
334
|
+
* - $ref: '#/parameters/clientIDParamPatch'
|
335
|
+
* - $ref: '#/parameters/clientSecretParamPatch'
|
336
|
+
* - $ref: '#/parameters/nameParamPatch'
|
337
|
+
* - $ref: '#/parameters/descriptionParamPatch'
|
338
|
+
* - $ref: '#/parameters/encodedRawMetadataParamPatch'
|
339
|
+
* - $ref: '#/parameters/rawMetadataParamPatch'
|
340
|
+
* - $ref: '#/parameters/oidcDiscoveryUrlPatch'
|
341
|
+
* - $ref: '#/parameters/oidcClientIdPatch'
|
342
|
+
* - $ref: '#/parameters/oidcClientSecretPatch'
|
343
|
+
* - $ref: '#/parameters/defaultRedirectUrlParamPatch'
|
344
|
+
* - $ref: '#/parameters/redirectUrlParamPatch'
|
345
|
+
* - $ref: '#/parameters/tenantParamPatch'
|
346
|
+
* - $ref: '#/parameters/productParamPatch'
|
294
347
|
* responses:
|
295
348
|
* 204:
|
296
349
|
* description: Success
|
297
350
|
* 400:
|
298
|
-
*
|
351
|
+
* $ref: '#/definitions/validationErrorsPatch'
|
299
352
|
* 401:
|
300
353
|
* description: Unauthorized
|
301
354
|
*/
|
302
|
-
|
355
|
+
updateSAMLConnection(body) {
|
303
356
|
return __awaiter(this, void 0, void 0, function* () {
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
357
|
+
yield saml_1.default.update(body, this.connectionStore, this.getConnections.bind(this));
|
358
|
+
});
|
359
|
+
}
|
360
|
+
// For backwards compatibility
|
361
|
+
updateConfig(...args) {
|
362
|
+
return __awaiter(this, void 0, void 0, function* () {
|
363
|
+
yield this.updateSAMLConnection(...args);
|
364
|
+
});
|
365
|
+
}
|
366
|
+
updateOIDCConnection(body) {
|
367
|
+
return __awaiter(this, void 0, void 0, function* () {
|
368
|
+
yield oidc_1.default.update(body, this.connectionStore, this.getConnections.bind(this));
|
369
|
+
});
|
370
|
+
}
|
371
|
+
/**
|
372
|
+
* @swagger
|
373
|
+
* parameters:
|
374
|
+
* tenantParamGet:
|
375
|
+
* in: query
|
376
|
+
* name: tenant
|
377
|
+
* type: string
|
378
|
+
* description: Tenant
|
379
|
+
* productParamGet:
|
380
|
+
* in: query
|
381
|
+
* name: product
|
382
|
+
* type: string
|
383
|
+
* description: Product
|
384
|
+
* clientIDParamGet:
|
385
|
+
* in: query
|
386
|
+
* name: clientID
|
387
|
+
* type: string
|
388
|
+
* description: Client ID
|
389
|
+
* definitions:
|
390
|
+
* Connection:
|
391
|
+
* type: object
|
392
|
+
* properties:
|
393
|
+
* clientID:
|
394
|
+
* type: string
|
395
|
+
* description: Connection clientID
|
396
|
+
* clientSecret:
|
397
|
+
* type: string
|
398
|
+
* description: Connection clientSecret
|
399
|
+
* name:
|
400
|
+
* type: string
|
401
|
+
* description: Connection name
|
402
|
+
* description:
|
403
|
+
* type: string
|
404
|
+
* description: Connection description
|
405
|
+
* redirectUrl:
|
406
|
+
* type: string
|
407
|
+
* description: A list of allowed redirect URLs
|
408
|
+
* defaultRedirectUrl:
|
409
|
+
* type: string
|
410
|
+
* description: The redirect URL to use in the IdP login flow
|
411
|
+
* tenant:
|
412
|
+
* type: string
|
413
|
+
* description: Connection tenant
|
414
|
+
* product:
|
415
|
+
* type: string
|
416
|
+
* description: Connection product
|
417
|
+
* idpMetadata:
|
418
|
+
* type: object
|
419
|
+
* description: SAML IdP metadata
|
420
|
+
* certs:
|
421
|
+
* type: object
|
422
|
+
* description: Certs generated for SAML connection
|
423
|
+
* oidcProvider:
|
424
|
+
* type: object
|
425
|
+
* description: OIDC IdP metadata
|
426
|
+
* responses:
|
427
|
+
* '200Get':
|
428
|
+
* description: Success
|
429
|
+
* schema:
|
430
|
+
* type: array
|
431
|
+
* items:
|
432
|
+
* $ref: '#/definitions/Connection'
|
433
|
+
* '400Get':
|
434
|
+
* description: Please provide `clientID` or `tenant` and `product`.
|
435
|
+
* '401Get':
|
436
|
+
* description: Unauthorized
|
437
|
+
* /api/v1/connections:
|
438
|
+
* get:
|
439
|
+
* summary: Get SSO Connections
|
440
|
+
* parameters:
|
441
|
+
* - $ref: '#/parameters/tenantParamGet'
|
442
|
+
* - $ref: '#/parameters/productParamGet'
|
443
|
+
* - $ref: '#/parameters/clientIDParamGet'
|
444
|
+
* operationId: get-connections
|
445
|
+
* tags: [Connections]
|
446
|
+
* responses:
|
447
|
+
* '200':
|
448
|
+
* $ref: '#/responses/200Get'
|
449
|
+
* '400':
|
450
|
+
* $ref: '#/responses/400Get'
|
451
|
+
* '401':
|
452
|
+
* $ref: '#/responses/401Get'
|
453
|
+
*/
|
454
|
+
getConnections(body) {
|
455
|
+
return __awaiter(this, void 0, void 0, function* () {
|
456
|
+
const clientID = 'clientID' in body ? body.clientID : undefined;
|
457
|
+
const tenant = 'tenant' in body ? body.tenant : undefined;
|
458
|
+
const product = 'product' in body ? body.product : undefined;
|
459
|
+
const strategy = 'strategy' in body ? body.strategy : undefined;
|
460
|
+
metrics.increment('getConnections');
|
461
|
+
if (clientID) {
|
462
|
+
const connection = yield this.connectionStore.get(clientID);
|
463
|
+
if (!connection || typeof connection !== 'object') {
|
464
|
+
return [];
|
333
465
|
}
|
334
|
-
|
466
|
+
return [connection];
|
335
467
|
}
|
336
|
-
if (
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
468
|
+
if (tenant && product) {
|
469
|
+
const connections = yield this.connectionStore.getByIndex({
|
470
|
+
name: utils_1.IndexNames.TenantProduct,
|
471
|
+
value: dbutils.keyFromParts(tenant, product),
|
472
|
+
});
|
473
|
+
if (!connections || !connections.length) {
|
474
|
+
return [];
|
475
|
+
}
|
476
|
+
// filter if strategy is passed
|
477
|
+
const filteredConnections = strategy
|
478
|
+
? connections.filter((connection) => {
|
479
|
+
if (strategy === 'saml') {
|
480
|
+
if (connection.idpMetadata) {
|
481
|
+
return true;
|
482
|
+
}
|
483
|
+
}
|
484
|
+
if (strategy === 'oidc') {
|
485
|
+
if (connection.oidcProvider) {
|
486
|
+
return true;
|
487
|
+
}
|
488
|
+
}
|
489
|
+
return false;
|
490
|
+
})
|
491
|
+
: connections;
|
492
|
+
if (!filteredConnections.length) {
|
493
|
+
return [];
|
341
494
|
}
|
495
|
+
return filteredConnections;
|
342
496
|
}
|
343
|
-
|
344
|
-
yield this.configStore.put(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID, record, {
|
345
|
-
// secondary index on entityID
|
346
|
-
name: utils_1.IndexNames.EntityID,
|
347
|
-
value: _currentConfig.idpMetadata.entityID,
|
348
|
-
}, {
|
349
|
-
// secondary index on tenant + product
|
350
|
-
name: utils_1.IndexNames.TenantProduct,
|
351
|
-
value: dbutils.keyFromParts(_currentConfig.tenant, _currentConfig.product),
|
352
|
-
});
|
497
|
+
throw new error_1.JacksonError('Please provide `clientID` or `tenant` and `product`.', 400);
|
353
498
|
});
|
354
499
|
}
|
355
500
|
/**
|
356
501
|
* @swagger
|
357
|
-
*
|
358
502
|
* /api/v1/saml/config:
|
359
503
|
* get:
|
360
|
-
* summary: Get SAML
|
504
|
+
* summary: Get SAML Config
|
361
505
|
* operationId: get-saml-config
|
362
|
-
* tags:
|
363
|
-
*
|
506
|
+
* tags: [SAML Config - Deprecated]
|
507
|
+
* deprecated: true
|
364
508
|
* parameters:
|
365
|
-
* -
|
366
|
-
*
|
367
|
-
*
|
368
|
-
* description: Tenant
|
369
|
-
* - in: query
|
370
|
-
* name: product
|
371
|
-
* type: string
|
372
|
-
* description: Product
|
373
|
-
* - in: query
|
374
|
-
* name: clientID
|
375
|
-
* type: string
|
376
|
-
* description: Client ID
|
509
|
+
* - $ref: '#/parameters/tenantParamGet'
|
510
|
+
* - $ref: '#/parameters/productParamGet'
|
511
|
+
* - $ref: '#/parameters/clientIDParamGet'
|
377
512
|
* responses:
|
378
|
-
*
|
513
|
+
* '200':
|
379
514
|
* description: Success
|
380
515
|
* schema:
|
381
516
|
* type: object
|
382
517
|
* example:
|
383
518
|
* {
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
387
|
-
*
|
388
|
-
*
|
389
|
-
*
|
390
|
-
*
|
391
|
-
*
|
392
|
-
*
|
393
|
-
*
|
394
|
-
*
|
395
|
-
*
|
396
|
-
*
|
397
|
-
*
|
398
|
-
*
|
399
|
-
*
|
400
|
-
*
|
401
|
-
*
|
402
|
-
*
|
403
|
-
*
|
404
|
-
*
|
405
|
-
*
|
406
|
-
*
|
407
|
-
*
|
408
|
-
*
|
409
|
-
*
|
410
|
-
*
|
519
|
+
* "idpMetadata": {
|
520
|
+
* "sso": {
|
521
|
+
* "postUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml",
|
522
|
+
* "redirectUrl": "https://dev-20901260.okta.com/app/dev-20901260_jacksonnext_1/xxxxxxxxxxxxx/sso/saml"
|
523
|
+
* },
|
524
|
+
* "entityID": "http://www.okta.com/xxxxxxxxxxxxx",
|
525
|
+
* "thumbprint": "Eo+eUi3UM3XIMkFFtdVK3yJ5vO9f7YZdasdasdad",
|
526
|
+
* "loginType": "idp",
|
527
|
+
* "provider": "okta.com"
|
528
|
+
* },
|
529
|
+
* "defaultRedirectUrl": "https://hoppscotch.io/",
|
530
|
+
* "redirectUrl": ["https://hoppscotch.io/"],
|
531
|
+
* "tenant": "hoppscotch.io",
|
532
|
+
* "product": "API Engine",
|
533
|
+
* "name": "Hoppscotch-SP",
|
534
|
+
* "description": "SP for hoppscotch.io",
|
535
|
+
* "clientID": "Xq8AJt3yYAxmXizsCWmUBDRiVP1iTC8Y/otnvFIMitk",
|
536
|
+
* "clientSecret": "00e3e11a3426f97d8000000738300009130cd45419c5943",
|
537
|
+
* "certs": {
|
538
|
+
* "publicKey": "-----BEGIN CERTIFICATE-----.......-----END CERTIFICATE-----",
|
539
|
+
* "privateKey": "-----BEGIN PRIVATE KEY-----......-----END PRIVATE KEY-----"
|
540
|
+
* }
|
541
|
+
* }
|
542
|
+
* '400':
|
543
|
+
* $ref: '#/responses/400Get'
|
544
|
+
* '401':
|
545
|
+
* $ref: '#/responses/401Get'
|
411
546
|
*/
|
412
547
|
getConfig(body) {
|
413
548
|
return __awaiter(this, void 0, void 0, function* () {
|
414
|
-
const
|
415
|
-
|
549
|
+
const clientID = 'clientID' in body ? body.clientID : undefined;
|
550
|
+
const tenant = 'tenant' in body ? body.tenant : undefined;
|
551
|
+
const product = 'product' in body ? body.product : undefined;
|
552
|
+
metrics.increment('getConnections');
|
416
553
|
if (clientID) {
|
417
|
-
const samlConfig = yield this.
|
554
|
+
const samlConfig = yield this.connectionStore.get(clientID);
|
418
555
|
return samlConfig || {};
|
419
556
|
}
|
420
557
|
if (tenant && product) {
|
421
|
-
const samlConfigs = yield this.
|
558
|
+
const samlConfigs = yield this.connectionStore.getByIndex({
|
422
559
|
name: utils_1.IndexNames.TenantProduct,
|
423
560
|
value: dbutils.keyFromParts(tenant, product),
|
424
561
|
});
|
@@ -432,52 +569,90 @@ class APIController {
|
|
432
569
|
}
|
433
570
|
/**
|
434
571
|
* @swagger
|
572
|
+
* parameters:
|
573
|
+
* clientIDDel:
|
574
|
+
* name: clientID
|
575
|
+
* in: formData
|
576
|
+
* type: string
|
577
|
+
* description: Client ID
|
578
|
+
* clientSecretDel:
|
579
|
+
* name: clientSecret
|
580
|
+
* in: formData
|
581
|
+
* type: string
|
582
|
+
* description: Client Secret
|
583
|
+
* tenantDel:
|
584
|
+
* name: tenant
|
585
|
+
* in: formData
|
586
|
+
* type: string
|
587
|
+
* description: Tenant
|
588
|
+
* productDel:
|
589
|
+
* name: product
|
590
|
+
* in: formData
|
591
|
+
* type: string
|
592
|
+
* description: Product
|
593
|
+
* strategyDel:
|
594
|
+
* name: strategy
|
595
|
+
* in: formData
|
596
|
+
* type: string
|
597
|
+
* description: Strategy
|
598
|
+
* /api/v1/connections:
|
599
|
+
* delete:
|
600
|
+
* parameters:
|
601
|
+
* - $ref: '#/parameters/clientIDDel'
|
602
|
+
* - $ref: '#/parameters/clientSecretDel'
|
603
|
+
* - $ref: '#/parameters/tenantDel'
|
604
|
+
* - $ref: '#/parameters/productDel'
|
605
|
+
* - $ref: '#/parameters/strategyDel'
|
606
|
+
* summary: Delete SSO Connections
|
607
|
+
* operationId: delete-sso-connection
|
608
|
+
* tags: [Connections]
|
609
|
+
* consumes:
|
610
|
+
* - application/x-www-form-urlencoded
|
611
|
+
* - application/json
|
612
|
+
* responses:
|
613
|
+
* '200':
|
614
|
+
* description: Success
|
615
|
+
* '400':
|
616
|
+
* description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.
|
617
|
+
* '401':
|
618
|
+
* description: Unauthorized
|
435
619
|
* /api/v1/saml/config:
|
436
620
|
* delete:
|
437
|
-
* summary: Delete SAML
|
621
|
+
* summary: Delete SAML Config
|
438
622
|
* operationId: delete-saml-config
|
439
|
-
* tags:
|
440
|
-
*
|
623
|
+
* tags: [SAML Config - Deprecated]
|
624
|
+
* deprecated: true
|
441
625
|
* consumes:
|
442
626
|
* - application/x-www-form-urlencoded
|
627
|
+
* - application/json
|
443
628
|
* parameters:
|
444
|
-
*
|
445
|
-
*
|
446
|
-
*
|
447
|
-
*
|
448
|
-
* description: Client ID
|
449
|
-
* - name: clientSecret
|
450
|
-
* in: formData
|
451
|
-
* type: string
|
452
|
-
* required: true
|
453
|
-
* description: Client Secret
|
454
|
-
* - name: tenant
|
455
|
-
* in: formData
|
456
|
-
* type: string
|
457
|
-
* description: Tenant
|
458
|
-
* - name: product
|
459
|
-
* in: formData
|
460
|
-
* type: string
|
461
|
-
* description: Product
|
629
|
+
* - $ref: '#/parameters/clientIDDel'
|
630
|
+
* - $ref: '#/parameters/clientSecretDel'
|
631
|
+
* - $ref: '#/parameters/tenantDel'
|
632
|
+
* - $ref: '#/parameters/productDel'
|
462
633
|
* responses:
|
463
634
|
* '200':
|
464
635
|
* description: Success
|
465
636
|
* '400':
|
466
|
-
* description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.
|
637
|
+
* description: clientSecret mismatch | Please provide `clientID` and `clientSecret` or `tenant` and `product`.
|
467
638
|
* '401':
|
468
639
|
* description: Unauthorized
|
469
640
|
*/
|
470
|
-
|
641
|
+
deleteConnections(body) {
|
471
642
|
return __awaiter(this, void 0, void 0, function* () {
|
472
|
-
const
|
473
|
-
|
643
|
+
const clientID = 'clientID' in body ? body.clientID : undefined;
|
644
|
+
const clientSecret = 'clientSecret' in body ? body.clientSecret : undefined;
|
645
|
+
const tenant = 'tenant' in body ? body.tenant : undefined;
|
646
|
+
const product = 'product' in body ? body.product : undefined;
|
647
|
+
const strategy = 'strategy' in body ? body.strategy : undefined;
|
648
|
+
metrics.increment('deleteConnections');
|
474
649
|
if (clientID && clientSecret) {
|
475
|
-
const
|
476
|
-
if (!
|
650
|
+
const connection = yield this.connectionStore.get(clientID);
|
651
|
+
if (!connection) {
|
477
652
|
return;
|
478
653
|
}
|
479
|
-
if (
|
480
|
-
yield this.
|
654
|
+
if (connection.clientSecret === clientSecret) {
|
655
|
+
yield this.connectionStore.delete(clientID);
|
481
656
|
}
|
482
657
|
else {
|
483
658
|
throw new error_1.JacksonError('clientSecret mismatch', 400);
|
@@ -485,47 +660,41 @@ class APIController {
|
|
485
660
|
return;
|
486
661
|
}
|
487
662
|
if (tenant && product) {
|
488
|
-
const
|
663
|
+
const connections = yield this.connectionStore.getByIndex({
|
489
664
|
name: utils_1.IndexNames.TenantProduct,
|
490
665
|
value: dbutils.keyFromParts(tenant, product),
|
491
666
|
});
|
492
|
-
if (!
|
667
|
+
if (!connections || !connections.length) {
|
493
668
|
return;
|
494
669
|
}
|
495
|
-
|
496
|
-
|
670
|
+
// filter if strategy is passed
|
671
|
+
const filteredConnections = strategy
|
672
|
+
? connections.filter((connection) => {
|
673
|
+
if (strategy === 'saml') {
|
674
|
+
if (connection.idpMetadata) {
|
675
|
+
return true;
|
676
|
+
}
|
677
|
+
}
|
678
|
+
if (strategy === 'oidc') {
|
679
|
+
if (connection.oidcProvider) {
|
680
|
+
return true;
|
681
|
+
}
|
682
|
+
}
|
683
|
+
return false;
|
684
|
+
})
|
685
|
+
: connections;
|
686
|
+
for (const conf of filteredConnections) {
|
687
|
+
yield this.connectionStore.delete(conf.clientID);
|
497
688
|
}
|
498
689
|
return;
|
499
690
|
}
|
500
691
|
throw new error_1.JacksonError('Please provide `clientID` and `clientSecret` or `tenant` and `product`.', 400);
|
501
692
|
});
|
502
693
|
}
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
const pUrl = new URL(url);
|
508
|
-
if (pUrl.hostname.startsWith('www.')) {
|
509
|
-
return pUrl.hostname.substring(4);
|
510
|
-
}
|
511
|
-
return pUrl.hostname;
|
512
|
-
}
|
513
|
-
catch (err) {
|
514
|
-
return null;
|
515
|
-
}
|
516
|
-
};
|
517
|
-
const extractRedirectUrls = (urls) => {
|
518
|
-
if (!urls) {
|
519
|
-
return [];
|
520
|
-
}
|
521
|
-
if (typeof urls === 'string') {
|
522
|
-
if (urls.startsWith('[')) {
|
523
|
-
// redirectUrl is a stringified array
|
524
|
-
return JSON.parse(urls);
|
525
|
-
}
|
526
|
-
// redirectUrl is a single URL
|
527
|
-
return [urls];
|
694
|
+
deleteConfig(body) {
|
695
|
+
return __awaiter(this, void 0, void 0, function* () {
|
696
|
+
yield this.deleteConnections(Object.assign(Object.assign({}, body), { strategy: 'saml' }));
|
697
|
+
});
|
528
698
|
}
|
529
|
-
|
530
|
-
|
531
|
-
};
|
699
|
+
}
|
700
|
+
exports.ConnectionAPIController = ConnectionAPIController;
|