rapidoc 9.3.7 → 10.0.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/README.md +193 -61
- package/dist/rapidoc-min.js +279 -3912
- package/package.json +24 -47
- package/.babelrc.json +0 -42
- package/.eslintrc +0 -53
- package/.github/FUNDING.yml +0 -13
- package/.github/workflow/words.yaml +0 -22
- package/CONTRIBUTING.md +0 -35
- package/dist/index.html +0 -6
- package/dist/index.html.gz +0 -0
- package/dist/rapidoc-min.js.LICENSE.txt +0 -64
- package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
- package/dist/rapidoc-min.js.gz +0 -0
- package/dist/rapidoc-min.js.map +0 -1
- package/dist/rapidoc-min.js.map.gz +0 -0
- package/dist/rapidoc.js +0 -21594
- package/dist/report.html +0 -39
- package/index-hashroute.html +0 -65
- package/index.html +0 -249
- package/index1.html +0 -244
- package/jsconfig.json +0 -11
- package/logo.png +0 -0
- package/performance.html +0 -31
- package/src/components/api-request.js +0 -1787
- package/src/components/api-response.js +0 -335
- package/src/components/dialog-box.js +0 -73
- package/src/components/json-tree.js +0 -145
- package/src/components/schema-table.js +0 -358
- package/src/components/schema-tree.js +0 -378
- package/src/components/tag-input.js +0 -135
- package/src/index.js +0 -7
- package/src/json-schema-viewer.js +0 -345
- package/src/oauth-receiver.js +0 -56
- package/src/rapidoc-mini.js +0 -380
- package/src/rapidoc.js +0 -1082
- package/src/styles/border-styles.js +0 -24
- package/src/styles/custom-styles.js +0 -10
- package/src/styles/dialog-box-styles.js +0 -75
- package/src/styles/endpoint-styles.js +0 -202
- package/src/styles/flex-styles.js +0 -15
- package/src/styles/font-styles.js +0 -268
- package/src/styles/info-styles.js +0 -21
- package/src/styles/input-styles.js +0 -202
- package/src/styles/nav-styles.js +0 -198
- package/src/styles/prism-styles.js +0 -105
- package/src/styles/schema-styles.js +0 -121
- package/src/styles/tab-styles.js +0 -47
- package/src/styles/table-styles.js +0 -58
- package/src/templates/advance-search-template.js +0 -72
- package/src/templates/callback-template.js +0 -73
- package/src/templates/code-samples-template.js +0 -34
- package/src/templates/components-template.js +0 -76
- package/src/templates/endpoint-template.js +0 -249
- package/src/templates/expanded-endpoint-template.js +0 -197
- package/src/templates/focused-endpoint-template.js +0 -110
- package/src/templates/header-template.js +0 -66
- package/src/templates/json-schema-viewer-template.js +0 -185
- package/src/templates/logo-template.js +0 -18
- package/src/templates/main-body-template.js +0 -121
- package/src/templates/navbar-template.js +0 -272
- package/src/templates/overview-template.js +0 -79
- package/src/templates/security-scheme-template.js +0 -610
- package/src/templates/server-template.js +0 -115
- package/src/utils/color-utils.js +0 -59
- package/src/utils/common-utils.js +0 -168
- package/src/utils/schema-utils.js +0 -1086
- package/src/utils/spec-parser.js +0 -462
- package/src/utils/theme.js +0 -288
- package/webpack.config.js +0 -142
|
@@ -1,610 +0,0 @@
|
|
|
1
|
-
/* eslint-disable arrow-body-style */
|
|
2
|
-
import { html } from 'lit';
|
|
3
|
-
import { unsafeHTML } from 'lit/directives/unsafe-html.js'; // eslint-disable-line import/extensions
|
|
4
|
-
import { marked } from 'marked';
|
|
5
|
-
|
|
6
|
-
const codeVerifier = '731DB1C3F7EA533B85E29492D26AA-1234567890-1234567890';
|
|
7
|
-
const codeChallenge = '4FatVDBJKPAo4JgLLaaQFMUcQPn5CrPRvLlaob9PTYc'; // Base64 encoded SHA-256
|
|
8
|
-
|
|
9
|
-
const localStorageKey = 'rapidoc';
|
|
10
|
-
|
|
11
|
-
export function applyApiKey(securitySchemeId, username = '', password = '', providedApikeyVal = '') {
|
|
12
|
-
const securityObj = this.resolvedSpec.securitySchemes?.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
13
|
-
if (!securityObj) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
let finalApiKeyValue = '';
|
|
17
|
-
if (securityObj.scheme?.toLowerCase() === 'basic') {
|
|
18
|
-
if (username) {
|
|
19
|
-
finalApiKeyValue = `Basic ${Buffer.from(`${username}:${password}`, 'utf8').toString('base64')}`;
|
|
20
|
-
}
|
|
21
|
-
} else if (providedApikeyVal) {
|
|
22
|
-
securityObj.value = providedApikeyVal;
|
|
23
|
-
finalApiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer ' : ''}${providedApikeyVal}`;
|
|
24
|
-
}
|
|
25
|
-
if (finalApiKeyValue) {
|
|
26
|
-
securityObj.finalKeyValue = finalApiKeyValue;
|
|
27
|
-
this.requestUpdate();
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function onClearAllApiKeys() {
|
|
34
|
-
this.resolvedSpec.securitySchemes?.forEach((v) => {
|
|
35
|
-
v.user = '';
|
|
36
|
-
v.password = '';
|
|
37
|
-
v.value = '';
|
|
38
|
-
v.finalKeyValue = '';
|
|
39
|
-
});
|
|
40
|
-
this.requestUpdate();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getPersistedApiKeys() {
|
|
44
|
-
return JSON.parse(localStorage.getItem(localStorageKey)) || {};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function setPersistedApiKeys(obj) {
|
|
48
|
-
localStorage.setItem(localStorageKey, JSON.stringify(obj));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function recoverPersistedApiKeys() {
|
|
52
|
-
const rapidocLs = getPersistedApiKeys.call(this);
|
|
53
|
-
Object.values(rapidocLs).forEach((p) => {
|
|
54
|
-
applyApiKey.call(this, p.securitySchemeId, p.username, p.password, p.value);
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function onApiKeyChange(securitySchemeId) {
|
|
59
|
-
let apiKeyValue = '';
|
|
60
|
-
const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
61
|
-
if (securityObj) {
|
|
62
|
-
const trEl = this.shadowRoot.getElementById(`security-scheme-${securitySchemeId}`);
|
|
63
|
-
if (trEl) {
|
|
64
|
-
if (securityObj.type && securityObj.scheme && securityObj.type === 'http' && securityObj.scheme.toLowerCase() === 'basic') {
|
|
65
|
-
const userVal = trEl.querySelector('.api-key-user').value.trim();
|
|
66
|
-
const passwordVal = trEl.querySelector('.api-key-password').value.trim();
|
|
67
|
-
applyApiKey.call(this, securitySchemeId, userVal, passwordVal);
|
|
68
|
-
} else {
|
|
69
|
-
apiKeyValue = trEl.querySelector('.api-key-input').value.trim();
|
|
70
|
-
applyApiKey.call(this, securitySchemeId, '', '', apiKeyValue);
|
|
71
|
-
}
|
|
72
|
-
if (this.persistAuth === 'true') {
|
|
73
|
-
const rapidocLs = getPersistedApiKeys.call(this);
|
|
74
|
-
rapidocLs[securitySchemeId] = securityObj;
|
|
75
|
-
setPersistedApiKeys.call(this, rapidocLs);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Updates the OAuth Access Token (API key), so it reflects in UI and gets used in TRY calls
|
|
82
|
-
function updateOAuthKey(securitySchemeId, accessToken, tokenType = 'Bearer') {
|
|
83
|
-
const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
84
|
-
securityObj.finalKeyValue = `${(tokenType.toLowerCase() === 'bearer' ? 'Bearer' : (tokenType.toLowerCase() === 'mac' ? 'MAC' : tokenType))} ${accessToken}`;
|
|
85
|
-
this.requestUpdate();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/* eslint-disable no-console */
|
|
89
|
-
// Gets Access-Token in exchange of Authorization Code
|
|
90
|
-
async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, grantType, authCode, securitySchemeId, authFlowDivEl, sendClientSecretIn = 'header', scopes = null, username = null, password = null) {
|
|
91
|
-
const respDisplayEl = authFlowDivEl ? authFlowDivEl.querySelector('.oauth-resp-display') : undefined;
|
|
92
|
-
const urlFormParams = new URLSearchParams();
|
|
93
|
-
const headers = new Headers();
|
|
94
|
-
urlFormParams.append('grant_type', grantType);
|
|
95
|
-
if (grantType === 'authorization_code') {
|
|
96
|
-
urlFormParams.append('client_id', clientId);
|
|
97
|
-
urlFormParams.append('client_secret', clientSecret);
|
|
98
|
-
}
|
|
99
|
-
if (grantType !== 'client_credentials' && grantType !== 'password') {
|
|
100
|
-
urlFormParams.append('redirect_uri', redirectUrl);
|
|
101
|
-
}
|
|
102
|
-
if (authCode) {
|
|
103
|
-
urlFormParams.append('code', authCode);
|
|
104
|
-
urlFormParams.append('code_verifier', codeVerifier); // for PKCE
|
|
105
|
-
}
|
|
106
|
-
if (sendClientSecretIn === 'header') {
|
|
107
|
-
headers.set('Authorization', `Basic ${Buffer.from(`${clientId}:${clientSecret}`, 'utf8').toString('base64')}`);
|
|
108
|
-
} else if (grantType !== 'authorization_code') {
|
|
109
|
-
urlFormParams.append('client_id', clientId);
|
|
110
|
-
urlFormParams.append('client_secret', clientSecret);
|
|
111
|
-
}
|
|
112
|
-
if (grantType === 'password') {
|
|
113
|
-
urlFormParams.append('username', username);
|
|
114
|
-
urlFormParams.append('password', password);
|
|
115
|
-
}
|
|
116
|
-
if (scopes) {
|
|
117
|
-
urlFormParams.append('scope', scopes);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const resp = await fetch(tokenUrl, { method: 'POST', headers, body: urlFormParams });
|
|
122
|
-
const tokenResp = await resp.json();
|
|
123
|
-
if (resp.ok) {
|
|
124
|
-
if (tokenResp.token_type && tokenResp.access_token) {
|
|
125
|
-
updateOAuthKey.call(this, securitySchemeId, tokenResp.access_token, tokenResp.token_type);
|
|
126
|
-
if (respDisplayEl) {
|
|
127
|
-
respDisplayEl.innerHTML = '<span style="color:var(--green)">Access Token Received</span>';
|
|
128
|
-
}
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
if (respDisplayEl) {
|
|
133
|
-
respDisplayEl.innerHTML = `<span style="color:var(--red)">${tokenResp.error_description || tokenResp.error_description || 'Unable to get access token'}</span>`;
|
|
134
|
-
}
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
} catch {
|
|
138
|
-
if (respDisplayEl) {
|
|
139
|
-
respDisplayEl.innerHTML = '<span style="color:var(--red)">Failed to get access token</span>';
|
|
140
|
-
}
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Gets invoked when it receives the Authorization Code from the other window via message-event
|
|
146
|
-
async function onWindowMessageEvent(msgEvent, winObj, tokenUrl, clientId, clientSecret, redirectUrl, grantType, sendClientSecretIn, securitySchemeId, authFlowDivEl) {
|
|
147
|
-
sessionStorage.removeItem('winMessageEventActive');
|
|
148
|
-
winObj.close();
|
|
149
|
-
if (msgEvent.data.fake) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (!msgEvent.data) {
|
|
153
|
-
console.warn('RapiDoc: Received no data with authorization message');
|
|
154
|
-
}
|
|
155
|
-
if (msgEvent.data.error) {
|
|
156
|
-
console.warn('RapiDoc: Error while receiving data');
|
|
157
|
-
}
|
|
158
|
-
if (msgEvent.data) {
|
|
159
|
-
if (msgEvent.data.responseType === 'code') {
|
|
160
|
-
// Authorization Code flow
|
|
161
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrl, grantType, msgEvent.data.code, securitySchemeId, authFlowDivEl, sendClientSecretIn);
|
|
162
|
-
} else if (msgEvent.data.responseType === 'token') {
|
|
163
|
-
// Implicit flow
|
|
164
|
-
updateOAuthKey.call(this, securitySchemeId, msgEvent.data.access_token, msgEvent.data.token_type);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// code_challenge generator for PKCE flow
|
|
170
|
-
// TODO: Implement dynamic generation of code-challenge based on code-verifier
|
|
171
|
-
/*
|
|
172
|
-
async function generateCodeChallenge() {
|
|
173
|
-
const encoder = new TextEncoder();
|
|
174
|
-
const data = encoder.encode(codeVerifier);
|
|
175
|
-
const sha256Hash = await window.crypto.subtle.digest('SHA-256', data); // returns Unit8Array
|
|
176
|
-
// const utf8Decoder = new TextDecoder();
|
|
177
|
-
// const b64EncodedSha256 = btoa(utf8Decoder.decode(sha256Hash));
|
|
178
|
-
const b64EncodedSha256 = base64encode(sha256Hash);
|
|
179
|
-
return b64EncodedSha256;
|
|
180
|
-
}
|
|
181
|
-
*/
|
|
182
|
-
|
|
183
|
-
async function onInvokeOAuthFlow(securitySchemeId, flowType, authUrl, tokenUrl, e) {
|
|
184
|
-
const authFlowDivEl = e.target.closest('.oauth-flow');
|
|
185
|
-
const clientId = authFlowDivEl.querySelector('.oauth-client-id') ? authFlowDivEl.querySelector('.oauth-client-id').value.trim() : '';
|
|
186
|
-
const clientSecret = authFlowDivEl.querySelector('.oauth-client-secret') ? authFlowDivEl.querySelector('.oauth-client-secret').value.trim() : '';
|
|
187
|
-
const username = authFlowDivEl.querySelector('.api-key-user') ? authFlowDivEl.querySelector('.api-key-user').value.trim() : '';
|
|
188
|
-
const password = authFlowDivEl.querySelector('.api-key-password') ? authFlowDivEl.querySelector('.api-key-password').value.trim() : '';
|
|
189
|
-
const sendClientSecretIn = authFlowDivEl.querySelector('.oauth-send-client-secret-in') ? authFlowDivEl.querySelector('.oauth-send-client-secret-in').value.trim() : 'header';
|
|
190
|
-
const checkedScopeEls = [...authFlowDivEl.querySelectorAll('.scope-checkbox:checked')];
|
|
191
|
-
const pkceCheckboxEl = authFlowDivEl.querySelector(`#${securitySchemeId}-pkce`);
|
|
192
|
-
const state = (`${Math.random().toString(36).slice(2, 9)}random${Math.random().toString(36).slice(2, 9)}`);
|
|
193
|
-
const nonce = (`${Math.random().toString(36).slice(2, 9)}random${Math.random().toString(36).slice(2, 9)}`);
|
|
194
|
-
// const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
195
|
-
const redirectUrlObj = new URL(`${window.location.origin}${window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/'))}/${this.oauthReceiver}`);
|
|
196
|
-
let grantType = '';
|
|
197
|
-
let responseType = '';
|
|
198
|
-
let newWindow;
|
|
199
|
-
|
|
200
|
-
// clear previous error messages
|
|
201
|
-
const errEls = [...authFlowDivEl.parentNode.querySelectorAll('.oauth-resp-display')];
|
|
202
|
-
errEls.forEach((v) => { v.innerHTML = ''; });
|
|
203
|
-
|
|
204
|
-
if (flowType === 'authorizationCode' || flowType === 'implicit') {
|
|
205
|
-
const authUrlObj = new URL(authUrl);
|
|
206
|
-
if (flowType === 'authorizationCode') {
|
|
207
|
-
grantType = 'authorization_code';
|
|
208
|
-
responseType = 'code';
|
|
209
|
-
} else if (flowType === 'implicit') {
|
|
210
|
-
responseType = 'token';
|
|
211
|
-
}
|
|
212
|
-
const authCodeParams = new URLSearchParams(authUrlObj.search);
|
|
213
|
-
const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
|
|
214
|
-
if (selectedScopes) {
|
|
215
|
-
authCodeParams.set('scope', selectedScopes);
|
|
216
|
-
}
|
|
217
|
-
authCodeParams.set('client_id', clientId);
|
|
218
|
-
authCodeParams.set('redirect_uri', redirectUrlObj.toString());
|
|
219
|
-
authCodeParams.set('response_type', responseType);
|
|
220
|
-
authCodeParams.set('state', state);
|
|
221
|
-
authCodeParams.set('nonce', nonce);
|
|
222
|
-
if (pkceCheckboxEl && pkceCheckboxEl.checked) {
|
|
223
|
-
authCodeParams.set('code_challenge', codeChallenge);
|
|
224
|
-
authCodeParams.set('code_challenge_method', 'S256');
|
|
225
|
-
}
|
|
226
|
-
authCodeParams.set('show_dialog', true);
|
|
227
|
-
authUrlObj.search = authCodeParams.toString();
|
|
228
|
-
// If any older message-event-listener is active then fire a fake message to remove it (these are single time listeners)
|
|
229
|
-
if (sessionStorage.getItem('winMessageEventActive') === 'true') {
|
|
230
|
-
window.postMessage({ fake: true }, this);
|
|
231
|
-
}
|
|
232
|
-
setTimeout(() => {
|
|
233
|
-
newWindow = window.open(authUrlObj.toString());
|
|
234
|
-
if (!newWindow) {
|
|
235
|
-
console.error(`RapiDoc: Unable to open ${authUrlObj.toString()} in a new window`);
|
|
236
|
-
} else {
|
|
237
|
-
sessionStorage.setItem('winMessageEventActive', 'true');
|
|
238
|
-
window.addEventListener(
|
|
239
|
-
'message',
|
|
240
|
-
(msgEvent) => onWindowMessageEvent.call(this, msgEvent, newWindow, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, sendClientSecretIn, securitySchemeId, authFlowDivEl),
|
|
241
|
-
{ once: true },
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
}, 10);
|
|
245
|
-
} else if (flowType === 'clientCredentials') {
|
|
246
|
-
grantType = 'client_credentials';
|
|
247
|
-
const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
|
|
248
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '', securitySchemeId, authFlowDivEl, sendClientSecretIn, selectedScopes);
|
|
249
|
-
} else if (flowType === 'password') {
|
|
250
|
-
grantType = 'password';
|
|
251
|
-
const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
|
|
252
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '', securitySchemeId, authFlowDivEl, sendClientSecretIn, selectedScopes, username, password);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
/* eslint-enable no-console */
|
|
256
|
-
|
|
257
|
-
/* eslint-disable indent */
|
|
258
|
-
|
|
259
|
-
function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, authFlow, defaultScopes = [], receiveTokenIn = 'header', receiveTokenInOptions = undefined) {
|
|
260
|
-
let { authorizationUrl, tokenUrl, refreshUrl } = authFlow;
|
|
261
|
-
const pkceOnly = authFlow['x-pkce-only'] || false;
|
|
262
|
-
const isUrlAbsolute = (url) => (url.indexOf('://') > 0 || url.indexOf('//') === 0);
|
|
263
|
-
// Calculcate base URL
|
|
264
|
-
const url = new URL(this.selectedServer?.computedUrl);
|
|
265
|
-
const baseUrl = url.origin;
|
|
266
|
-
|
|
267
|
-
if (refreshUrl && !isUrlAbsolute(refreshUrl)) {
|
|
268
|
-
refreshUrl = `${baseUrl}/${refreshUrl.replace(/^\//, '')}`;
|
|
269
|
-
}
|
|
270
|
-
if (tokenUrl && !isUrlAbsolute(tokenUrl)) {
|
|
271
|
-
tokenUrl = `${baseUrl}/${tokenUrl.replace(/^\//, '')}`;
|
|
272
|
-
}
|
|
273
|
-
if (authorizationUrl && !isUrlAbsolute(authorizationUrl)) {
|
|
274
|
-
authorizationUrl = `${baseUrl}/${authorizationUrl.replace(/^\//, '')}`;
|
|
275
|
-
}
|
|
276
|
-
let flowNameDisplay;
|
|
277
|
-
if (flowName === 'authorizationCode') {
|
|
278
|
-
flowNameDisplay = 'Authorization Code Flow';
|
|
279
|
-
} else if (flowName === 'clientCredentials') {
|
|
280
|
-
flowNameDisplay = 'Client Credentials Flow';
|
|
281
|
-
} else if (flowName === 'implicit') {
|
|
282
|
-
flowNameDisplay = 'Implicit Flow';
|
|
283
|
-
} else if (flowName === 'password') {
|
|
284
|
-
flowNameDisplay = 'Password Flow';
|
|
285
|
-
} else {
|
|
286
|
-
flowNameDisplay = flowName;
|
|
287
|
-
}
|
|
288
|
-
return html`
|
|
289
|
-
<div class="oauth-flow ${flowName}" style="padding: 12px 0; margin-bottom:12px;">
|
|
290
|
-
<div class="tiny-title upper" style="margin-bottom:8px;">${flowNameDisplay}</div>
|
|
291
|
-
${authorizationUrl
|
|
292
|
-
? html`<div style="margin-bottom:5px"><span style="width:75px; display: inline-block;">Auth URL</span> <span class="mono-font"> ${authorizationUrl} </span></div>`
|
|
293
|
-
: ''
|
|
294
|
-
}
|
|
295
|
-
${tokenUrl
|
|
296
|
-
? html`<div style="margin-bottom:5px"><span style="width:75px; display: inline-block;">Token URL</span> <span class="mono-font">${tokenUrl}</span></div>`
|
|
297
|
-
: ''
|
|
298
|
-
}
|
|
299
|
-
${refreshUrl
|
|
300
|
-
? html`<div style="margin-bottom:5px"><span style="width:75px; display: inline-block;">Refresh URL</span> <span class="mono-font">${refreshUrl}</span></div>`
|
|
301
|
-
: ''
|
|
302
|
-
}
|
|
303
|
-
${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'implicit' || flowName === 'password'
|
|
304
|
-
? html`
|
|
305
|
-
${authFlow.scopes
|
|
306
|
-
? html`
|
|
307
|
-
<span> Scopes </span>
|
|
308
|
-
<div class= "oauth-scopes" part="section-auth-scopes" style = "width:100%; display:flex; flex-direction:column; flex-wrap:wrap; margin:0 0 10px 24px">
|
|
309
|
-
${Object.entries(authFlow.scopes).map((scopeAndDescr, index) => html`
|
|
310
|
-
<div class="m-checkbox" style="display:inline-flex; align-items:center">
|
|
311
|
-
<input type="checkbox" part="checkbox checkbox-auth-scope" class="scope-checkbox" id="${securitySchemeId}${flowName}${index}" ?checked="${defaultScopes.includes(scopeAndDescr[0])}" value="${scopeAndDescr[0]}">
|
|
312
|
-
<label for="${securitySchemeId}${flowName}${index}" style="margin-left:5px; cursor:pointer">
|
|
313
|
-
<span class="mono-font">${scopeAndDescr[0]}</span>
|
|
314
|
-
${scopeAndDescr[0] !== scopeAndDescr[1] ? ` - ${scopeAndDescr[1] || ''}` : ''}
|
|
315
|
-
</label>
|
|
316
|
-
</div>
|
|
317
|
-
`)}
|
|
318
|
-
</div>
|
|
319
|
-
`
|
|
320
|
-
: ''
|
|
321
|
-
}
|
|
322
|
-
${flowName === 'password'
|
|
323
|
-
? html`
|
|
324
|
-
<div style="margin:5px 0">
|
|
325
|
-
<input type="text" value = "" placeholder="username" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-user" part="textbox textbox-username" id="input-${securitySchemeId}-${flowName}-api-key-user">
|
|
326
|
-
<input type="password" value = "" placeholder="password" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-password" style = "margin:0 5px;" part="textbox textbox-password" id="input-${securitySchemeId}-${flowName}-api-key-password">
|
|
327
|
-
</div>`
|
|
328
|
-
: ''
|
|
329
|
-
}
|
|
330
|
-
<div>
|
|
331
|
-
${flowName === 'authorizationCode'
|
|
332
|
-
? html`
|
|
333
|
-
<div style="margin: 16px 0 4px">
|
|
334
|
-
<input type="checkbox" part="checkbox checkbox-auth-scope" id="${securitySchemeId}-pkce" checked ?disabled=${pkceOnly}>
|
|
335
|
-
<label for="${securitySchemeId}-pkce" style="margin:0 16px 0 4px; line-height:24px; cursor:pointer">
|
|
336
|
-
Send Proof Key for Code Exchange (PKCE)
|
|
337
|
-
</label>
|
|
338
|
-
</div>
|
|
339
|
-
`
|
|
340
|
-
: ''
|
|
341
|
-
}
|
|
342
|
-
<input type="text" part="textbox textbox-auth-client-id" value = "${clientId || ''}" placeholder="client-id" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} oauth-client-id">
|
|
343
|
-
${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'password'
|
|
344
|
-
? html`
|
|
345
|
-
<input
|
|
346
|
-
id="${securitySchemeId}-${flowName}-oauth-client-secret"
|
|
347
|
-
type="password" part="textbox textbox-auth-client-secret"
|
|
348
|
-
value = "${clientSecret || ''}" placeholder="client-secret" spellcheck="false"
|
|
349
|
-
class="oauth2 ${flowName} ${securitySchemeId}
|
|
350
|
-
oauth-client-secret"
|
|
351
|
-
style = "margin:0 5px;${pkceOnly ? 'display:none;' : ''}"
|
|
352
|
-
>
|
|
353
|
-
<select style="margin-right:5px;${pkceOnly ? 'display:none;' : ''}" class="${flowName} ${securitySchemeId} oauth-send-client-secret-in">
|
|
354
|
-
${(!receiveTokenInOptions || receiveTokenInOptions.includes('header')) ? html`<option value = 'header' .selected = ${receiveTokenIn === 'header'} > Authorization Header </option>` : ''}
|
|
355
|
-
${(!receiveTokenInOptions || receiveTokenInOptions.includes('request-body')) ? html` <option value = 'request-body' .selected = ${receiveTokenIn === 'request-body'}> Request Body </option>` : ''}
|
|
356
|
-
</select>`
|
|
357
|
-
: ''
|
|
358
|
-
}
|
|
359
|
-
${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'implicit' || flowName === 'password'
|
|
360
|
-
? html`
|
|
361
|
-
<button class="m-btn thin-border" part="btn btn-outline"
|
|
362
|
-
@click="${(e) => { onInvokeOAuthFlow.call(this, securitySchemeId, flowName, authorizationUrl, tokenUrl, e); }}"
|
|
363
|
-
> GET TOKEN </button>`
|
|
364
|
-
: ''
|
|
365
|
-
}
|
|
366
|
-
</div>
|
|
367
|
-
<div class="oauth-resp-display red-text small-font-size"></div>
|
|
368
|
-
`
|
|
369
|
-
: ''
|
|
370
|
-
}
|
|
371
|
-
</div>
|
|
372
|
-
`;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function removeApiKey(securitySchemeId) {
|
|
376
|
-
const securityObj = this.resolvedSpec.securitySchemes?.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
377
|
-
securityObj.user = '';
|
|
378
|
-
securityObj.password = '';
|
|
379
|
-
securityObj.value = '';
|
|
380
|
-
securityObj.finalKeyValue = '';
|
|
381
|
-
if (this.persistAuth === 'true') {
|
|
382
|
-
const rapidocLs = getPersistedApiKeys.call(this);
|
|
383
|
-
delete rapidocLs[securityObj.securitySchemeId];
|
|
384
|
-
setPersistedApiKeys.call(this, rapidocLs);
|
|
385
|
-
}
|
|
386
|
-
this.requestUpdate();
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export default function securitySchemeTemplate() {
|
|
390
|
-
if (!this.resolvedSpec) { return ''; }
|
|
391
|
-
const providedApiKeys = this.resolvedSpec.securitySchemes?.filter((v) => (v.finalKeyValue));
|
|
392
|
-
if (!providedApiKeys) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
return html`
|
|
396
|
-
<section id='auth' part="section-auth" style="text-align:left; direction:ltr; margin-top:24px; margin-bottom:24px;" class = 'observe-me ${'read focused'.includes(this.renderStyle) ? 'section-gap--read-mode' : 'section-gap '}'>
|
|
397
|
-
<div class='sub-title regular-font'> AUTHENTICATION </div>
|
|
398
|
-
|
|
399
|
-
<div class="small-font-size" style="display:flex; align-items: center; min-height:30px">
|
|
400
|
-
${providedApiKeys.length > 0
|
|
401
|
-
? html`
|
|
402
|
-
<div class="blue-text"> ${providedApiKeys.length} API key applied </div>
|
|
403
|
-
<div style="flex:1"></div>
|
|
404
|
-
<button class="m-btn thin-border" part="btn btn-outline" @click=${() => { onClearAllApiKeys.call(this); }}>CLEAR ALL API KEYS</button>`
|
|
405
|
-
: html`<div class="red-text">No API key applied</div>`
|
|
406
|
-
}
|
|
407
|
-
</div>
|
|
408
|
-
${this.resolvedSpec.securitySchemes && this.resolvedSpec.securitySchemes.length > 0
|
|
409
|
-
? html`
|
|
410
|
-
<table role="presentation" id="auth-table" class='m-table padded-12' style="width:100%;">
|
|
411
|
-
${this.resolvedSpec.securitySchemes.map((v) => html`
|
|
412
|
-
<tr id="security-scheme-${v.securitySchemeId}" class="${v.type.toLowerCase()}">
|
|
413
|
-
<td style="max-width:500px; overflow-wrap: break-word;">
|
|
414
|
-
<div style="line-height:28px; margin-bottom:5px;">
|
|
415
|
-
<span style="font-weight:bold; font-size:var(--font-size-regular)">${v.typeDisplay}</span>
|
|
416
|
-
${v.finalKeyValue
|
|
417
|
-
? html`
|
|
418
|
-
<span class='blue-text'> ${v.finalKeyValue ? 'Key Applied' : ''} </span>
|
|
419
|
-
<button class="m-btn thin-border small" part="btn btn-outline" @click=${() => { removeApiKey.call(this, v.securitySchemeId); }}>REMOVE</button>
|
|
420
|
-
`
|
|
421
|
-
: ''
|
|
422
|
-
}
|
|
423
|
-
</div>
|
|
424
|
-
${v.description
|
|
425
|
-
? html`
|
|
426
|
-
<div class="m-markdown">
|
|
427
|
-
${unsafeHTML(marked(v.description || ''))}
|
|
428
|
-
</div>`
|
|
429
|
-
: ''
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
${(v.type.toLowerCase() === 'apikey') || (v.type.toLowerCase() === 'http' && v.scheme.toLowerCase() === 'bearer')
|
|
433
|
-
? html`
|
|
434
|
-
<div style="margin-bottom:5px">
|
|
435
|
-
${v.type.toLowerCase() === 'apikey'
|
|
436
|
-
? html`Send <code>${v.name}</code> in <code>${v.in}</code>`
|
|
437
|
-
: html`Send <code>Authorization</code> in <code>header</code> containing the word <code>Bearer</code> followed by a space and a Token String.`
|
|
438
|
-
}
|
|
439
|
-
</div>
|
|
440
|
-
<div style="max-height:28px;">
|
|
441
|
-
${v.in !== 'cookie'
|
|
442
|
-
? html`
|
|
443
|
-
<input type = "text" value = "${v.value}" class="${v.type} ${v.securitySchemeId} api-key-input" placeholder = "api-token" spellcheck = "false" id = "${v.type}-${v.securitySchemeId}-api-key-input">
|
|
444
|
-
<button class="m-btn thin-border" style = "margin-left:5px;"
|
|
445
|
-
part = "btn btn-outline"
|
|
446
|
-
@click="${(e) => { onApiKeyChange.call(this, v.securitySchemeId, e); }}">
|
|
447
|
-
${v.finalKeyValue ? 'UPDATE' : 'SET'}
|
|
448
|
-
</button>`
|
|
449
|
-
: html`<span class="gray-text" style="font-size::var(--font-size-small)"> cookies cannot be set from here</span>`
|
|
450
|
-
}
|
|
451
|
-
</div>`
|
|
452
|
-
: ''
|
|
453
|
-
}
|
|
454
|
-
${v.type.toLowerCase() === 'http' && v.scheme.toLowerCase() === 'basic'
|
|
455
|
-
? html`
|
|
456
|
-
<div style="margin-bottom:5px">
|
|
457
|
-
Send <code>Authorization</code> in <code>header</code> containing the word <code>Basic</code> followed by a space and a base64 encoded string of <code>username:password</code>.
|
|
458
|
-
</div>
|
|
459
|
-
<div>
|
|
460
|
-
<input type="text" value = "${v.user}" placeholder="username" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-user" style="width:100px" id = "input-${v.type}-${v.securitySchemeId}-api-key-user">
|
|
461
|
-
<input type="password" value = "${v.password}" placeholder="password" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-password" style = "width:100px; margin:0 5px;" id = "input-${v.type}-${v.securitySchemeId}-api-key-password">
|
|
462
|
-
<button class="m-btn thin-border"
|
|
463
|
-
@click="${(e) => { onApiKeyChange.call(this, v.securitySchemeId, e); }}"
|
|
464
|
-
part = "btn btn-outline"
|
|
465
|
-
>
|
|
466
|
-
${v.finalKeyValue ? 'UPDATE' : 'SET'}
|
|
467
|
-
</button>
|
|
468
|
-
</div>`
|
|
469
|
-
: ''
|
|
470
|
-
}
|
|
471
|
-
</td>
|
|
472
|
-
</tr>
|
|
473
|
-
${v.type.toLowerCase() === 'oauth2'
|
|
474
|
-
? html`
|
|
475
|
-
<tr>
|
|
476
|
-
<td style="border:none; padding-left:48px">
|
|
477
|
-
${Object.keys(v.flows).map((f) => oAuthFlowTemplate
|
|
478
|
-
.call(
|
|
479
|
-
this,
|
|
480
|
-
f,
|
|
481
|
-
(v.flows[f]['x-client-id'] || v['x-client-id'] || ''),
|
|
482
|
-
(v.flows[f]['x-client-secret'] || v['x-client-secret'] || ''),
|
|
483
|
-
v.securitySchemeId,
|
|
484
|
-
v.flows[f],
|
|
485
|
-
(v.flows[f]['x-default-scopes'] || v['x-default-scopes']),
|
|
486
|
-
(v.flows[f]['x-receive-token-in'] || v['x-receive-token-in']),
|
|
487
|
-
(v.flows[f]['x-receive-token-in-options'] || v['x-receive-token-in-options']),
|
|
488
|
-
))}
|
|
489
|
-
</td>
|
|
490
|
-
</tr>
|
|
491
|
-
`
|
|
492
|
-
: ''
|
|
493
|
-
}
|
|
494
|
-
`)}
|
|
495
|
-
</table>`
|
|
496
|
-
: ''
|
|
497
|
-
}
|
|
498
|
-
<slot name="auth"></slot>
|
|
499
|
-
</section>
|
|
500
|
-
`;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
export function pathSecurityTemplate(pathSecurity) {
|
|
504
|
-
if (this.resolvedSpec.securitySchemes && pathSecurity) {
|
|
505
|
-
const orSecurityKeys1 = [];
|
|
506
|
-
if (Array.isArray(pathSecurity)) {
|
|
507
|
-
if (pathSecurity.length === 0) {
|
|
508
|
-
return '';
|
|
509
|
-
}
|
|
510
|
-
} else {
|
|
511
|
-
return '';
|
|
512
|
-
}
|
|
513
|
-
pathSecurity.forEach((pSecurity) => {
|
|
514
|
-
const andSecurityKeys1 = [];
|
|
515
|
-
const andKeyTypes = [];
|
|
516
|
-
if (Object.keys(pSecurity).length === 0) {
|
|
517
|
-
orSecurityKeys1.push({
|
|
518
|
-
securityTypes: 'None',
|
|
519
|
-
securityDefs: [],
|
|
520
|
-
});
|
|
521
|
-
} else {
|
|
522
|
-
Object.keys(pSecurity).forEach((pathSecurityKey) => {
|
|
523
|
-
let pathScopes = '';
|
|
524
|
-
const s = this.resolvedSpec.securitySchemes.find((ss) => ss.securitySchemeId === pathSecurityKey);
|
|
525
|
-
if (pSecurity[pathSecurityKey] && Array.isArray(pSecurity[pathSecurityKey])) {
|
|
526
|
-
pathScopes = pSecurity[pathSecurityKey].join(', ');
|
|
527
|
-
}
|
|
528
|
-
if (s) {
|
|
529
|
-
andKeyTypes.push(s.typeDisplay);
|
|
530
|
-
andSecurityKeys1.push({ ...s, ...({ scopes: pathScopes }) });
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
orSecurityKeys1.push({
|
|
534
|
-
securityTypes: andKeyTypes.length > 1 ? `${andKeyTypes[0]} + ${andKeyTypes.length - 1} more` : andKeyTypes[0],
|
|
535
|
-
securityDefs: andSecurityKeys1,
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
return html`<div style="position:absolute; top:3px; right:2px; font-size:var(--font-size-small); line-height: 1.5;">
|
|
540
|
-
<div style="position:relative; display:flex; min-width:350px; max-width:700px; justify-content: flex-end;">
|
|
541
|
-
<svg width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" fill="none" style="stroke:var(--fg3)"> <rect x="5" y="11" width="14" height="10" rx="2" /> <circle cx="12" cy="16" r="1" /> <path d="M8 11v-4a4 4 0 0 1 8 0v4" /></svg>
|
|
542
|
-
${orSecurityKeys1.map((orSecurityItem1, i) => html`
|
|
543
|
-
${orSecurityItem1.securityTypes
|
|
544
|
-
? html`
|
|
545
|
-
${i !== 0 ? html`<div style="padding:3px 4px;"> OR </div>` : ''}
|
|
546
|
-
<div class="tooltip">
|
|
547
|
-
<div style = "padding:2px 4px; white-space:nowrap; text-overflow:ellipsis;max-width:150px; overflow:hidden;">
|
|
548
|
-
${this.updateRoute === 'true' && this.allowAuthentication === 'true'
|
|
549
|
-
? html`<a part="anchor anchor-operation-security" href="#auth"> ${orSecurityItem1.securityTypes} </a>`
|
|
550
|
-
: html`${orSecurityItem1.securityTypes}`
|
|
551
|
-
}
|
|
552
|
-
</div>
|
|
553
|
-
<div class="tooltip-text" style="position:absolute; color: var(--fg); top:26px; right:0; border:1px solid var(--border-color);padding:2px 4px; display:block;">
|
|
554
|
-
${orSecurityItem1.securityDefs.length > 1 ? html`<div>Requires <b>all</b> of the following </div>` : ''}
|
|
555
|
-
<div style="padding-left: 8px">
|
|
556
|
-
${orSecurityItem1.securityDefs.map((andSecurityItem, j) => {
|
|
557
|
-
const scopeHtml = html`${andSecurityItem.scopes !== ''
|
|
558
|
-
? html`
|
|
559
|
-
<div>
|
|
560
|
-
<b>Required scopes:</b>
|
|
561
|
-
<br/>
|
|
562
|
-
<div style="margin-left:8px">
|
|
563
|
-
${andSecurityItem.scopes.split(',').map((scope, cnt) => html`${cnt === 0 ? '' : '┃'}<span>${scope}</span>`)}
|
|
564
|
-
</div>
|
|
565
|
-
</div>`
|
|
566
|
-
: ''
|
|
567
|
-
}`;
|
|
568
|
-
|
|
569
|
-
return html`
|
|
570
|
-
${andSecurityItem.type === 'oauth2'
|
|
571
|
-
? html`
|
|
572
|
-
<div>
|
|
573
|
-
${orSecurityItem1.securityDefs.length > 1
|
|
574
|
-
? html`<b>${j + 1}.</b> `
|
|
575
|
-
: 'Needs'
|
|
576
|
-
}
|
|
577
|
-
OAuth Token <span style="font-family:var(--font-mono); color:var(--primary-color);">${andSecurityItem.securitySchemeId}</span> in <b>Authorization header</b>
|
|
578
|
-
${scopeHtml}
|
|
579
|
-
</div>`
|
|
580
|
-
: andSecurityItem.type === 'http'
|
|
581
|
-
? html`
|
|
582
|
-
<div>
|
|
583
|
-
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
584
|
-
${andSecurityItem.scheme === 'basic' ? 'Base 64 encoded username:password' : 'Bearer Token'} in <b>Authorization header</b>
|
|
585
|
-
${scopeHtml}
|
|
586
|
-
</div>`
|
|
587
|
-
: html`
|
|
588
|
-
<div>
|
|
589
|
-
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
590
|
-
Token in <b>${andSecurityItem.name} ${andSecurityItem.in}</b>
|
|
591
|
-
${scopeHtml}
|
|
592
|
-
</div>`
|
|
593
|
-
}`;
|
|
594
|
-
})}
|
|
595
|
-
</div>
|
|
596
|
-
</div>
|
|
597
|
-
</div>
|
|
598
|
-
`
|
|
599
|
-
: ''
|
|
600
|
-
}
|
|
601
|
-
`)
|
|
602
|
-
}
|
|
603
|
-
</div>
|
|
604
|
-
</div>
|
|
605
|
-
`;
|
|
606
|
-
}
|
|
607
|
-
return '';
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/* eslint-enable indent */
|