@boxyhq/saml-jackson 1.2.1 → 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 -376
- 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 +368 -118
- package/dist/controller/utils.d.ts +10 -2
- package/dist/controller/utils.js +88 -1
- package/dist/directory-sync/DirectoryUsers.js +4 -0
- 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/saml/x509.d.ts +4 -4
- package/dist/saml/x509.js +38 -42
- package/dist/typings.d.ts +110 -34
- package/package.json +14 -14
- package/dist/read-config.d.ts +0 -3
package/dist/controller/api.js
CHANGED
@@ -31,392 +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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
idpMetadata.provider = providerName ? providerName : 'Unknown';
|
197
|
-
const clientID = dbutils.keyDigest(dbutils.keyFromParts(tenant, product, idpMetadata.entityID));
|
198
|
-
let clientSecret;
|
199
|
-
const exists = yield this.configStore.get(clientID);
|
200
|
-
if (exists) {
|
201
|
-
clientSecret = exists.clientSecret;
|
202
|
-
}
|
203
|
-
else {
|
204
|
-
clientSecret = crypto_1.default.randomBytes(24).toString('hex');
|
205
|
-
}
|
206
|
-
const certs = yield x509_1.default.generate();
|
207
|
-
if (!certs) {
|
208
|
-
throw new Error('Error generating x59 certs');
|
209
|
-
}
|
210
|
-
const record = {
|
211
|
-
idpMetadata,
|
212
|
-
defaultRedirectUrl,
|
213
|
-
redirectUrl: redirectUrlList,
|
214
|
-
tenant,
|
215
|
-
product,
|
216
|
-
name,
|
217
|
-
description,
|
218
|
-
clientID,
|
219
|
-
clientSecret,
|
220
|
-
certs,
|
221
|
-
};
|
222
|
-
yield this.configStore.put(clientID, record, {
|
223
|
-
// secondary index on entityID
|
224
|
-
name: utils_1.IndexNames.EntityID,
|
225
|
-
value: idpMetadata.entityID,
|
226
|
-
}, {
|
227
|
-
// secondary index on tenant + product
|
228
|
-
name: utils_1.IndexNames.TenantProduct,
|
229
|
-
value: dbutils.keyFromParts(tenant, product),
|
230
|
-
});
|
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);
|
231
220
|
return record;
|
232
221
|
});
|
233
222
|
}
|
234
223
|
/**
|
235
224
|
* @swagger
|
236
|
-
*
|
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
|
237
298
|
* /api/v1/saml/config:
|
238
299
|
* patch:
|
239
|
-
* summary: Update SAML
|
300
|
+
* summary: Update SAML Config
|
240
301
|
* operationId: update-saml-config
|
241
|
-
* 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]
|
242
330
|
* consumes:
|
243
331
|
* - application/json
|
244
332
|
* - application/x-www-form-urlencoded
|
245
333
|
* parameters:
|
246
|
-
* -
|
247
|
-
*
|
248
|
-
*
|
249
|
-
*
|
250
|
-
*
|
251
|
-
* -
|
252
|
-
*
|
253
|
-
*
|
254
|
-
*
|
255
|
-
*
|
256
|
-
* -
|
257
|
-
*
|
258
|
-
*
|
259
|
-
* in: formData
|
260
|
-
* - name: description
|
261
|
-
* description: A short description for the config not more than 100 characters
|
262
|
-
* type: string
|
263
|
-
* in: formData
|
264
|
-
* - name: encodedRawMetadata
|
265
|
-
* description: Base64 encoding of the XML metadata
|
266
|
-
* in: formData
|
267
|
-
* type: string
|
268
|
-
* - name: rawMetadata
|
269
|
-
* description: Raw XML metadata
|
270
|
-
* in: formData
|
271
|
-
* type: string
|
272
|
-
* - name: defaultRedirectUrl
|
273
|
-
* description: The redirect URL to use in the IdP login flow
|
274
|
-
* in: formData
|
275
|
-
* required: true
|
276
|
-
* type: string
|
277
|
-
* - name: redirectUrl
|
278
|
-
* description: JSON encoded array containing a list of allowed redirect URLs
|
279
|
-
* in: formData
|
280
|
-
* required: true
|
281
|
-
* type: string
|
282
|
-
* - name: tenant
|
283
|
-
* description: Tenant
|
284
|
-
* in: formData
|
285
|
-
* required: true
|
286
|
-
* type: string
|
287
|
-
* - name: product
|
288
|
-
* description: Product
|
289
|
-
* in: formData
|
290
|
-
* required: true
|
291
|
-
* 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'
|
292
347
|
* responses:
|
293
348
|
* 204:
|
294
349
|
* description: Success
|
295
350
|
* 400:
|
296
|
-
*
|
351
|
+
* $ref: '#/definitions/validationErrorsPatch'
|
297
352
|
* 401:
|
298
353
|
* description: Unauthorized
|
299
354
|
*/
|
300
|
-
|
355
|
+
updateSAMLConnection(body) {
|
301
356
|
return __awaiter(this, void 0, void 0, function* () {
|
302
|
-
|
303
|
-
|
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
|
-
|
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 [];
|
331
465
|
}
|
332
|
-
|
466
|
+
return [connection];
|
333
467
|
}
|
334
|
-
if (
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
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 [];
|
339
494
|
}
|
495
|
+
return filteredConnections;
|
340
496
|
}
|
341
|
-
|
342
|
-
yield this.configStore.put(clientInfo === null || clientInfo === void 0 ? void 0 : clientInfo.clientID, record, {
|
343
|
-
// secondary index on entityID
|
344
|
-
name: utils_1.IndexNames.EntityID,
|
345
|
-
value: _currentConfig.idpMetadata.entityID,
|
346
|
-
}, {
|
347
|
-
// secondary index on tenant + product
|
348
|
-
name: utils_1.IndexNames.TenantProduct,
|
349
|
-
value: dbutils.keyFromParts(_currentConfig.tenant, _currentConfig.product),
|
350
|
-
});
|
497
|
+
throw new error_1.JacksonError('Please provide `clientID` or `tenant` and `product`.', 400);
|
351
498
|
});
|
352
499
|
}
|
353
500
|
/**
|
354
501
|
* @swagger
|
355
|
-
*
|
356
502
|
* /api/v1/saml/config:
|
357
503
|
* get:
|
358
|
-
* summary: Get SAML
|
504
|
+
* summary: Get SAML Config
|
359
505
|
* operationId: get-saml-config
|
360
|
-
* tags:
|
361
|
-
*
|
506
|
+
* tags: [SAML Config - Deprecated]
|
507
|
+
* deprecated: true
|
362
508
|
* parameters:
|
363
|
-
* -
|
364
|
-
*
|
365
|
-
*
|
366
|
-
* description: Tenant
|
367
|
-
* - in: query
|
368
|
-
* name: product
|
369
|
-
* type: string
|
370
|
-
* description: Product
|
371
|
-
* - in: query
|
372
|
-
* name: clientID
|
373
|
-
* type: string
|
374
|
-
* description: Client ID
|
509
|
+
* - $ref: '#/parameters/tenantParamGet'
|
510
|
+
* - $ref: '#/parameters/productParamGet'
|
511
|
+
* - $ref: '#/parameters/clientIDParamGet'
|
375
512
|
* responses:
|
376
|
-
*
|
513
|
+
* '200':
|
377
514
|
* description: Success
|
378
515
|
* schema:
|
379
516
|
* type: object
|
380
517
|
* example:
|
381
518
|
* {
|
382
|
-
*
|
383
|
-
*
|
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
|
-
*
|
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'
|
409
546
|
*/
|
410
547
|
getConfig(body) {
|
411
548
|
return __awaiter(this, void 0, void 0, function* () {
|
412
|
-
const
|
413
|
-
|
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');
|
414
553
|
if (clientID) {
|
415
|
-
const samlConfig = yield this.
|
554
|
+
const samlConfig = yield this.connectionStore.get(clientID);
|
416
555
|
return samlConfig || {};
|
417
556
|
}
|
418
557
|
if (tenant && product) {
|
419
|
-
const samlConfigs = yield this.
|
558
|
+
const samlConfigs = yield this.connectionStore.getByIndex({
|
420
559
|
name: utils_1.IndexNames.TenantProduct,
|
421
560
|
value: dbutils.keyFromParts(tenant, product),
|
422
561
|
});
|
@@ -430,52 +569,90 @@ class APIController {
|
|
430
569
|
}
|
431
570
|
/**
|
432
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
|
433
619
|
* /api/v1/saml/config:
|
434
620
|
* delete:
|
435
|
-
* summary: Delete SAML
|
621
|
+
* summary: Delete SAML Config
|
436
622
|
* operationId: delete-saml-config
|
437
|
-
* tags:
|
438
|
-
*
|
623
|
+
* tags: [SAML Config - Deprecated]
|
624
|
+
* deprecated: true
|
439
625
|
* consumes:
|
440
626
|
* - application/x-www-form-urlencoded
|
627
|
+
* - application/json
|
441
628
|
* parameters:
|
442
|
-
*
|
443
|
-
*
|
444
|
-
*
|
445
|
-
*
|
446
|
-
* description: Client ID
|
447
|
-
* - name: clientSecret
|
448
|
-
* in: formData
|
449
|
-
* type: string
|
450
|
-
* required: true
|
451
|
-
* description: Client Secret
|
452
|
-
* - name: tenant
|
453
|
-
* in: formData
|
454
|
-
* type: string
|
455
|
-
* description: Tenant
|
456
|
-
* - name: product
|
457
|
-
* in: formData
|
458
|
-
* type: string
|
459
|
-
* description: Product
|
629
|
+
* - $ref: '#/parameters/clientIDDel'
|
630
|
+
* - $ref: '#/parameters/clientSecretDel'
|
631
|
+
* - $ref: '#/parameters/tenantDel'
|
632
|
+
* - $ref: '#/parameters/productDel'
|
460
633
|
* responses:
|
461
634
|
* '200':
|
462
635
|
* description: Success
|
463
636
|
* '400':
|
464
|
-
* 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`.
|
465
638
|
* '401':
|
466
639
|
* description: Unauthorized
|
467
640
|
*/
|
468
|
-
|
641
|
+
deleteConnections(body) {
|
469
642
|
return __awaiter(this, void 0, void 0, function* () {
|
470
|
-
const
|
471
|
-
|
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');
|
472
649
|
if (clientID && clientSecret) {
|
473
|
-
const
|
474
|
-
if (!
|
650
|
+
const connection = yield this.connectionStore.get(clientID);
|
651
|
+
if (!connection) {
|
475
652
|
return;
|
476
653
|
}
|
477
|
-
if (
|
478
|
-
yield this.
|
654
|
+
if (connection.clientSecret === clientSecret) {
|
655
|
+
yield this.connectionStore.delete(clientID);
|
479
656
|
}
|
480
657
|
else {
|
481
658
|
throw new error_1.JacksonError('clientSecret mismatch', 400);
|
@@ -483,47 +660,41 @@ class APIController {
|
|
483
660
|
return;
|
484
661
|
}
|
485
662
|
if (tenant && product) {
|
486
|
-
const
|
663
|
+
const connections = yield this.connectionStore.getByIndex({
|
487
664
|
name: utils_1.IndexNames.TenantProduct,
|
488
665
|
value: dbutils.keyFromParts(tenant, product),
|
489
666
|
});
|
490
|
-
if (!
|
667
|
+
if (!connections || !connections.length) {
|
491
668
|
return;
|
492
669
|
}
|
493
|
-
|
494
|
-
|
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);
|
495
688
|
}
|
496
689
|
return;
|
497
690
|
}
|
498
691
|
throw new error_1.JacksonError('Please provide `clientID` and `clientSecret` or `tenant` and `product`.', 400);
|
499
692
|
});
|
500
693
|
}
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
const pUrl = new URL(url);
|
506
|
-
if (pUrl.hostname.startsWith('www.')) {
|
507
|
-
return pUrl.hostname.substring(4);
|
508
|
-
}
|
509
|
-
return pUrl.hostname;
|
510
|
-
}
|
511
|
-
catch (err) {
|
512
|
-
return null;
|
513
|
-
}
|
514
|
-
};
|
515
|
-
const extractRedirectUrls = (urls) => {
|
516
|
-
if (!urls) {
|
517
|
-
return [];
|
518
|
-
}
|
519
|
-
if (typeof urls === 'string') {
|
520
|
-
if (urls.startsWith('[')) {
|
521
|
-
// redirectUrl is a stringified array
|
522
|
-
return JSON.parse(urls);
|
523
|
-
}
|
524
|
-
// redirectUrl is a single URL
|
525
|
-
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
|
+
});
|
526
698
|
}
|
527
|
-
|
528
|
-
|
529
|
-
};
|
699
|
+
}
|
700
|
+
exports.ConnectionAPIController = ConnectionAPIController;
|