mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-27 08:35:22 +00:00
391 lines
15 KiB
JavaScript
391 lines
15 KiB
JavaScript
// swagger-ui-init.js
|
|
// https://github.com/axnsan12/drf-yasg
|
|
// Copyright 2017 - 2021, Cristian V. <cristi@cvjd.me>
|
|
// This file is licensed under the BSD 3-Clause License.
|
|
// License text available at https://opensource.org/licenses/BSD-3-Clause
|
|
|
|
"use strict";
|
|
var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
|
var defaultSpecUrl = currentPath + '?format=openapi';
|
|
|
|
function slugify(text) {
|
|
return text.toString().toLowerCase()
|
|
.replace(/\s+/g, '-') // Replace spaces with -
|
|
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
|
.replace(/--+/g, '-') // Replace multiple - with single -
|
|
.replace(/^-+/, '') // Trim - from start of text
|
|
.replace(/-+$/, ''); // Trim - from end of text
|
|
}
|
|
|
|
var KEY_AUTH = slugify(window.location.pathname) + "-drf-yasg-auth";
|
|
|
|
// load the saved authorization state from localStorage; ImmutableJS is used for consistency with swagger-ui state
|
|
var savedAuth = Immutable.fromJS({});
|
|
|
|
// global SwaggerUI config object; can be changed directly or by hooking initSwaggerUiConfig
|
|
var swaggerUiConfig = {
|
|
url: defaultSpecUrl,
|
|
dom_id: '#swagger-ui',
|
|
displayRequestDuration: true,
|
|
presets: [
|
|
SwaggerUIBundle.presets.apis,
|
|
SwaggerUIStandalonePreset
|
|
],
|
|
plugins: [
|
|
SwaggerUIBundle.plugins.DownloadUrl
|
|
],
|
|
layout: "StandaloneLayout",
|
|
filter: true,
|
|
requestInterceptor: function (request) {
|
|
var headers = request.headers || {};
|
|
var csrftoken = document.querySelector("[name=csrfmiddlewaretoken]");
|
|
if (csrftoken) {
|
|
headers["X-CSRFToken"] = csrftoken.value;
|
|
}
|
|
|
|
return request;
|
|
}
|
|
};
|
|
|
|
function patchSwaggerUi() {
|
|
if (document.querySelector('.auth-wrapper #django-session-auth')) {
|
|
return;
|
|
}
|
|
|
|
var authWrapper = document.querySelector('.auth-wrapper');
|
|
var authorizeButton = document.querySelector('.auth-wrapper .authorize');
|
|
var djangoSessionAuth = document.querySelector('#django-session-auth');
|
|
|
|
if (!djangoSessionAuth) {
|
|
console.log("WARNING: session auth disabled");
|
|
return;
|
|
}
|
|
|
|
djangoSessionAuth = djangoSessionAuth.cloneNode(true);
|
|
authWrapper.insertBefore(djangoSessionAuth, authorizeButton);
|
|
djangoSessionAuth.classList.remove("hidden");
|
|
}
|
|
|
|
function initSwaggerUi() {
|
|
if (window.ui) {
|
|
console.log("WARNING: skipping initSwaggerUi() because window.ui is already defined");
|
|
return;
|
|
}
|
|
if (document.querySelector('.auth-wrapper .authorize')) {
|
|
patchSwaggerUi();
|
|
} else {
|
|
insertionQ('.auth-wrapper .authorize').every(patchSwaggerUi);
|
|
}
|
|
|
|
var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML);
|
|
|
|
var oauth2RedirectUrl = document.getElementById('oauth2-redirect-url');
|
|
if (oauth2RedirectUrl) {
|
|
if (!('oauth2RedirectUrl' in swaggerSettings)) {
|
|
if (oauth2RedirectUrl) {
|
|
swaggerSettings['oauth2RedirectUrl'] = oauth2RedirectUrl.href;
|
|
}
|
|
}
|
|
oauth2RedirectUrl.parentNode.removeChild(oauth2RedirectUrl);
|
|
}
|
|
|
|
console.log('swaggerSettings', swaggerSettings);
|
|
var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML);
|
|
console.log('oauth2Config', oauth2Config);
|
|
|
|
initSwaggerUiConfig(swaggerSettings, oauth2Config);
|
|
window.ui = SwaggerUIBundle(swaggerUiConfig);
|
|
window.ui.initOAuth(oauth2Config);
|
|
}
|
|
|
|
/**
|
|
* Initialize the global swaggerUiConfig with any given additional settings.
|
|
* @param swaggerSettings SWAGGER_SETTINGS from Django settings
|
|
* @param oauth2Settings OAUTH2_CONFIG from Django settings
|
|
*/
|
|
function initSwaggerUiConfig(swaggerSettings, oauth2Settings) {
|
|
var persistAuth = swaggerSettings.persistAuth;
|
|
var refetchWithAuth = swaggerSettings.refetchWithAuth;
|
|
var refetchOnLogout = swaggerSettings.refetchOnLogout;
|
|
var fetchSchemaWithQuery = swaggerSettings.fetchSchemaWithQuery;
|
|
delete swaggerSettings['persistAuth'];
|
|
delete swaggerSettings['refetchWithAuth'];
|
|
delete swaggerSettings['refetchOnLogout'];
|
|
delete swaggerSettings['fetchSchemaWithQuery'];
|
|
|
|
for (var p in swaggerSettings) {
|
|
if (swaggerSettings.hasOwnProperty(p)) {
|
|
swaggerUiConfig[p] = swaggerSettings[p];
|
|
}
|
|
}
|
|
|
|
var specURL = swaggerUiConfig.url;
|
|
if (fetchSchemaWithQuery) {
|
|
// only add query params from document for the first spec request
|
|
// this ensures we otherwise honor the spec selector box which might be manually modified
|
|
var query = new URLSearchParams(window.location.search || '').entries();
|
|
for (var it = query.next(); !it.done; it = query.next()) {
|
|
specURL = setQueryParam(specURL, it.value[0], it.value[1]);
|
|
}
|
|
}
|
|
if (persistAuth) {
|
|
try {
|
|
savedAuth = Immutable.fromJS(JSON.parse(localStorage.getItem(KEY_AUTH)) || {});
|
|
} catch (e) {
|
|
localStorage.removeItem(KEY_AUTH);
|
|
}
|
|
}
|
|
if (refetchWithAuth) {
|
|
specURL = applyAuth(savedAuth, specURL) || specURL;
|
|
}
|
|
swaggerUiConfig.url = specURL;
|
|
|
|
if (persistAuth || refetchWithAuth) {
|
|
var hookedAuth = false;
|
|
|
|
var oldOnComplete = swaggerUiConfig.onComplete;
|
|
swaggerUiConfig.onComplete = function () {
|
|
if (persistAuth) {
|
|
preauthorizeAll(savedAuth, window.ui);
|
|
}
|
|
|
|
if (!hookedAuth) {
|
|
hookAuthActions(window.ui, persistAuth, refetchWithAuth, refetchOnLogout);
|
|
hookedAuth = true;
|
|
}
|
|
if (oldOnComplete) {
|
|
oldOnComplete();
|
|
}
|
|
};
|
|
|
|
var specRequestsInFlight = {};
|
|
var oldRequestInterceptor = swaggerUiConfig.requestInterceptor;
|
|
swaggerUiConfig.requestInterceptor = function (request) {
|
|
var headers = request.headers || {};
|
|
if (request.loadSpec) {
|
|
var newUrl = request.url;
|
|
if (refetchWithAuth) {
|
|
newUrl = applyAuth(savedAuth, newUrl, headers) || newUrl;
|
|
}
|
|
|
|
if (newUrl !== request.url) {
|
|
request.url = newUrl;
|
|
|
|
if (window.ui) {
|
|
// this visually updates the spec url before the request is done, i.e. while loading
|
|
window.ui.specActions.updateUrl(request.url);
|
|
} else {
|
|
// setTimeout is needed here because the request interceptor can be called *during*
|
|
// window.ui initialization (by the SwaggerUIBundle constructor)
|
|
setTimeout(function () {
|
|
window.ui.specActions.updateUrl(request.url);
|
|
});
|
|
}
|
|
|
|
// need to manually remember requests for spec urls because
|
|
// responseInterceptor has no reference to the request...
|
|
var absUrl = new URL(request.url, currentPath);
|
|
specRequestsInFlight[absUrl.href] = request.url;
|
|
}
|
|
}
|
|
|
|
if (oldRequestInterceptor) {
|
|
request = oldRequestInterceptor(request);
|
|
}
|
|
return request;
|
|
};
|
|
|
|
var oldResponseInterceptor = swaggerUiConfig.responseInterceptor;
|
|
swaggerUiConfig.responseInterceptor = function (response) {
|
|
var absUrl = new URL(response.url, currentPath);
|
|
if (absUrl.href in specRequestsInFlight) {
|
|
var setToUrl = specRequestsInFlight[absUrl.href];
|
|
delete specRequestsInFlight[absUrl.href];
|
|
if (response.ok) {
|
|
// need setTimeout here because swagger-ui insists to call updateUrl
|
|
// with the initial request url after the response...
|
|
setTimeout(function () {
|
|
var currentUrl = new URL(window.ui.specSelectors.url(), currentPath);
|
|
if (currentUrl.href !== absUrl.href) {
|
|
window.ui.specActions.updateUrl(setToUrl);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if (oldResponseInterceptor) {
|
|
response = oldResponseInterceptor(response);
|
|
}
|
|
return response;
|
|
}
|
|
}
|
|
}
|
|
|
|
function _usp(url, fn) {
|
|
url = url.split('?');
|
|
var usp = new URLSearchParams(url[1] || '');
|
|
fn(usp);
|
|
url[1] = usp.toString();
|
|
return url[1] ? url.join('?') : url[0];
|
|
}
|
|
|
|
function setQueryParam(url, key, value) {
|
|
return _usp(url, function (usp) {
|
|
usp.set(key, value);
|
|
});
|
|
}
|
|
|
|
function removeQueryParam(url, key) {
|
|
return _usp(url, function (usp) {
|
|
usp.delete(key);
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Call sui.preauthorize### for all authorizations in authorization.
|
|
* @param authorization authorization object {key => authScheme} saved from authActions.authorize
|
|
* @param sui SwaggerUI or SwaggerUIBundle instance
|
|
*/
|
|
function preauthorizeAll(authorization, sui) {
|
|
authorization.valueSeq().forEach(function (authScheme) {
|
|
var schemeName = authScheme.get("name"), schemeType = authScheme.getIn(["schema", "type"]);
|
|
if (schemeType === "basic" && schemeName) {
|
|
var username = authScheme.getIn(["value", "username"]);
|
|
var password = authScheme.getIn(["value", "password"]);
|
|
if (username && password) {
|
|
sui.preauthorizeBasic(schemeName, username, password);
|
|
}
|
|
} else if (schemeType === "apiKey" && schemeName) {
|
|
var key = authScheme.get("value");
|
|
if (key) {
|
|
sui.preauthorizeApiKey(schemeName, key);
|
|
}
|
|
} else {
|
|
// TODO: OAuth2
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Manually apply auth headers from the given auth object.
|
|
* @param {object} authorization authorization object {key => authScheme} saved from authActions.authorize
|
|
* @param {string} requestUrl the request url
|
|
* @param {object} requestHeaders target headers, modified in place by the function
|
|
* @return string new request url
|
|
*/
|
|
function applyAuth(authorization, requestUrl, requestHeaders) {
|
|
authorization.valueSeq().forEach(function (authScheme) {
|
|
requestHeaders = requestHeaders || {};
|
|
var schemeName = authScheme.get("name"), schemeType = authScheme.getIn(["schema", "type"]);
|
|
if (schemeType === "basic" && schemeName) {
|
|
var username = authScheme.getIn(["value", "username"]);
|
|
var password = authScheme.getIn(["value", "password"]);
|
|
if (username && password) {
|
|
requestHeaders["Authorization"] = "Basic " + btoa(username + ":" + password);
|
|
}
|
|
} else if (schemeType === "apiKey" && schemeName) {
|
|
var _in = authScheme.getIn(["schema", "in"]), paramName = authScheme.getIn(["schema", "name"]);
|
|
var key = authScheme.get("value");
|
|
if (key && paramName) {
|
|
if (_in === "header") {
|
|
requestHeaders[paramName] = key;
|
|
}
|
|
if (_in === "query") {
|
|
if (requestUrl) {
|
|
requestUrl = setQueryParam(requestUrl, paramName, key);
|
|
} else {
|
|
console.warn("WARNING: cannot apply apiKey query parameter via interceptor");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// TODO: OAuth2
|
|
}
|
|
});
|
|
|
|
return requestUrl;
|
|
}
|
|
|
|
/**
|
|
* Remove the given authorization scheme from the url.
|
|
* @param {object} authorization authorization object {key => authScheme} containing schemes to deauthorize
|
|
* @param {string} requestUrl request url
|
|
* @return string new request url
|
|
*/
|
|
function deauthUrl(authorization, requestUrl) {
|
|
authorization.valueSeq().forEach(function (authScheme) {
|
|
var schemeType = authScheme.getIn(["schema", "type"]);
|
|
if (schemeType === "apiKey") {
|
|
var _in = authScheme.getIn(["schema", "in"]), paramName = authScheme.getIn(["schema", "name"]);
|
|
if (_in === "query" && requestUrl && paramName) {
|
|
requestUrl = removeQueryParam(requestUrl, paramName);
|
|
}
|
|
} else {
|
|
// TODO: OAuth2?
|
|
}
|
|
});
|
|
return requestUrl;
|
|
}
|
|
|
|
/**
|
|
* Hook the authorize and logout actions of SwaggerUI.
|
|
* The hooks are used to persist authorization data and trigger schema refetch.
|
|
* @param sui SwaggerUI or SwaggerUIBundle instance
|
|
* @param {boolean} persistAuth true to save auth to local storage
|
|
* @param {boolean} refetchWithAuth true to trigger schema fetch on login
|
|
* @param {boolean} refetchOnLogout true to trigger schema fetch on logout
|
|
*/
|
|
function hookAuthActions(sui, persistAuth, refetchWithAuth, refetchOnLogout) {
|
|
if (!persistAuth && !refetchWithAuth) {
|
|
// nothing to do
|
|
return;
|
|
}
|
|
|
|
var originalAuthorize = sui.authActions.authorize;
|
|
sui.authActions.authorize = function (authorization) {
|
|
originalAuthorize(authorization);
|
|
// authorization is map of scheme name to scheme object
|
|
// need to use ImmutableJS because schema is already an ImmutableJS object
|
|
var newAuths = Immutable.fromJS(authorization);
|
|
savedAuth = savedAuth.merge(newAuths);
|
|
|
|
if (refetchWithAuth) {
|
|
var url = sui.specSelectors.url();
|
|
url = applyAuth(savedAuth, url) || url;
|
|
sui.specActions.updateUrl(url);
|
|
sui.specActions.download();
|
|
sui.authActions.showDefinitions(); // hide authorize dialog
|
|
}
|
|
if (persistAuth) {
|
|
localStorage.setItem(KEY_AUTH, JSON.stringify(savedAuth.toJSON()));
|
|
}
|
|
};
|
|
|
|
var originalLogout = sui.authActions.logout;
|
|
sui.authActions.logout = function (authorization) {
|
|
// stash logged out methods for use with deauthUrl
|
|
var loggedOut = savedAuth.filter(function (val, key) {
|
|
return authorization.indexOf(key) !== -1;
|
|
}).mapEntries(function (entry) {
|
|
return [entry[0], entry[1].set("value", null)]
|
|
});
|
|
// remove logged out methods from savedAuth
|
|
savedAuth = savedAuth.filter(function (val, key) {
|
|
return authorization.indexOf(key) === -1;
|
|
});
|
|
|
|
if (refetchWithAuth) {
|
|
var url = sui.specSelectors.url();
|
|
url = deauthUrl(loggedOut, url) || url;
|
|
sui.specActions.updateUrl(url);
|
|
sui.specActions.download(url);
|
|
sui.authActions.showDefinitions(); // hide authorize dialog
|
|
}
|
|
if (persistAuth) {
|
|
localStorage.setItem(KEY_AUTH, JSON.stringify(savedAuth.toJSON()));
|
|
}
|
|
originalLogout(authorization);
|
|
};
|
|
}
|
|
|
|
window.addEventListener('load', initSwaggerUi);
|