Add swagger-ui auth hooks

Save authorization to local storage, refetch spec on auth, etc.
master
Cristi Vîjdea 2018-10-14 04:28:42 +03:00
parent d41f0c5ac4
commit a419eec071
9 changed files with 5103 additions and 52 deletions

View File

@ -27,12 +27,14 @@
</content> </content>
<orderEntry type="jdk" jdkName="Python 3 (drf-yasg)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3 (drf-yasg)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="immutable" level="application" />
</component> </component>
<component name="TemplatesService"> <component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" /> <option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS"> <option name="TEMPLATE_FOLDERS">
<list> <list>
<option value="$MODULE_DIR$/src/drf_yasg/templates" /> <option value="$MODULE_DIR$/src/drf_yasg/templates" />
<option value="$MODULE_DIR$/testproj/testproj/templates" />
</list> </list>
</option> </option>
</component> </component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{immutable}" />
</component>
</project>

View File

@ -33,7 +33,7 @@ SWAGGER_DEFAULTS = {
'USE_SESSION_AUTH': True, 'USE_SESSION_AUTH': True,
'SECURITY_DEFINITIONS': { 'SECURITY_DEFINITIONS': {
'basic': { 'Basic': {
'type': 'basic' 'type': 'basic'
} }
}, },

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,27 +1,60 @@
"use strict"; "use strict";
var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname; var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname;
var specURL = currentPath + '?format=openapi'; var defaultSpecUrl = currentPath + '?format=openapi';
var savedAuth = Immutable.fromJS({});
try {
savedAuth = Immutable.fromJS(JSON.parse(localStorage.getItem("drf-yasg-auth")) || {});
} catch (e) {
localStorage.removeItem("drf-yasg-auth");
}
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;
}
if (request.loadSpec) {
applyAuth(savedAuth, headers);
}
return request;
},
onComplete: function () {
preauthorizeAny(savedAuth, window.ui);
hookAuthActions(window.ui);
},
};
function patchSwaggerUi() { function patchSwaggerUi() {
if (document.querySelector('.auth-wrapper #django-session-auth')) {
return;
}
var authWrapper = document.querySelector('.auth-wrapper'); var authWrapper = document.querySelector('.auth-wrapper');
var authorizeButton = document.querySelector('.auth-wrapper .authorize'); var authorizeButton = document.querySelector('.auth-wrapper .authorize');
var djangoSessionAuth = document.querySelector('#django-session-auth'); var djangoSessionAuth = document.querySelector('#django-session-auth');
if (!djangoSessionAuth) { if (!djangoSessionAuth) {
console.log("WARNING: session auth disabled"); console.log("WARNING: session auth disabled");
return; return;
} }
if (document.querySelector('.auth-wrapper #django-session-auth')) { djangoSessionAuth = djangoSessionAuth.cloneNode(true);
console.log("WARNING: session auth already patched; skipping patchSwaggerUi()");
return;
}
authWrapper.insertBefore(djangoSessionAuth, authorizeButton); authWrapper.insertBefore(djangoSessionAuth, authorizeButton);
djangoSessionAuth.classList.remove("hidden"); djangoSessionAuth.classList.remove("hidden");
var divider = document.createElement("div");
divider.classList.add("divider");
authWrapper.insertBefore(divider, authorizeButton);
} }
function initSwaggerUi() { function initSwaggerUi() {
@ -29,28 +62,6 @@ function initSwaggerUi() {
console.log("WARNING: skipping initSwaggerUi() because window.ui is already defined"); console.log("WARNING: skipping initSwaggerUi() because window.ui is already defined");
return; return;
} }
var swaggerConfig = {
url: specURL,
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;
}
};
var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML); var swaggerSettings = JSON.parse(document.getElementById('swagger-settings').innerHTML);
if (!('oauth2RedirectUrl' in swaggerSettings)) { if (!('oauth2RedirectUrl' in swaggerSettings)) {
@ -64,23 +75,82 @@ function initSwaggerUi() {
console.log('swaggerSettings', swaggerSettings); console.log('swaggerSettings', swaggerSettings);
for (var p in swaggerSettings) { for (var p in swaggerSettings) {
if (swaggerSettings.hasOwnProperty(p)) { if (swaggerSettings.hasOwnProperty(p)) {
swaggerConfig[p] = swaggerSettings[p]; swaggerUiConfig[p] = swaggerSettings[p];
} }
} }
window.ui = SwaggerUIBundle(swaggerConfig);
var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML); var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML);
console.log('oauth2Config', oauth2Config); console.log('oauth2Config', oauth2Config);
window.ui = SwaggerUIBundle(swaggerUiConfig);
window.ui.initOAuth(oauth2Config); window.ui.initOAuth(oauth2Config);
} }
window.onload = function () { function preauthorizeAny(savedAuth, sui) {
initSwaggerUi(); var schemeName = savedAuth.get("name"), schemeType = savedAuth.getIn(["schema", "type"]);
}; if (schemeType === "basic" && schemeName) {
var username = savedAuth.getIn(["value", "username"]);
var password = savedAuth.getIn(["value", "password"]);
if (username && password) {
sui.preauthorizeBasic(schemeName, username, password);
}
} else if (schemeType === "apiKey" && schemeName) {
var key = savedAuth.get("value");
if (key) {
sui.preauthorizeApiKey(schemeName, key);
}
}
}
if (document.querySelector('.auth-wrapper .authorize')) { function applyAuth(savedAuth, requestHeaders) {
patchSwaggerUi(); var schemeName = savedAuth.get("name"), schemeType = savedAuth.getIn(["schema", "type"]);
if (schemeType === "basic" && schemeName) {
var username = savedAuth.getIn(["value", "username"]);
var password = savedAuth.getIn(["value", "password"]);
if (username && password) {
requestHeaders["Authorization"] = "Basic " + btoa(username + ":" + password);
}
} else if (schemeType === "apiKey" && schemeName) {
var key = savedAuth.get("value"), _in = savedAuth.getIn(["schema", "in"]);
var paramName = savedAuth.getIn(["schema", "name"]);
if (key && paramName && _in === "header") {
requestHeaders[paramName] = key;
}
if (_in === "query") {
console.warn("WARNING: cannot apply apiKey query parameter via interceptor");
}
}
} }
else {
insertionQ('.auth-wrapper .authorize').every(patchSwaggerUi); function hookAuthActions(sui) {
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 schemes = Immutable.fromJS(authorization);
var auth = schemes.valueSeq().first();
localStorage.setItem("drf-yasg-auth", JSON.stringify(auth.toJSON()));
savedAuth = auth;
sui.specActions.download();
};
var originalLogout = sui.authActions.logout;
sui.authActions.logout = function (authorization) {
if (savedAuth.get("name") === authorization[0]) {
localStorage.removeItem("drf-yasg-auth");
savedAuth = Immutable.fromJS({});
}
originalLogout(authorization);
};
} }
window.addEventListener('load', function () {
initSwaggerUi();
if (document.querySelector('.auth-wrapper .authorize')) {
patchSwaggerUi();
}
else {
insertionQ('.auth-wrapper .authorize').every(patchSwaggerUi);
}
});

View File

@ -26,6 +26,7 @@
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-bundle.js' %}"></script> <script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-bundle.js' %}"></script>
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script> <script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script>
<script src="{% static 'drf-yasg/insQ.min.js' %}"></script> <script src="{% static 'drf-yasg/insQ.min.js' %}"></script>
<script src="{% static 'drf-yasg/immutable.js' %}"></script>
<script src="{% static 'drf-yasg/swagger-ui-init.js' %}"></script> <script src="{% static 'drf-yasg/swagger-ui-init.js' %}"></script>
{% block extra_scripts %} {% block extra_scripts %}
{# -- Add any additional scripts here -- #} {# -- Add any additional scripts here -- #}

View File

@ -49,7 +49,7 @@ ROOT_URLCONF = 'testproj.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 'DIRS': [os.path.join(BASE_DIR, 'testproj', 'templates')],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -132,6 +132,9 @@ USE_TZ = True
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'testproj', 'static'),
]
# Testing # Testing

View File

@ -7,6 +7,7 @@ wget https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js -O src/drf_yas
cp -r node_modules/swagger-ui-dist src/drf_yasg/static/drf-yasg/ cp -r node_modules/swagger-ui-dist src/drf_yasg/static/drf-yasg/
pushd src/drf_yasg/static/drf-yasg/swagger-ui-dist/ >/dev/null pushd src/drf_yasg/static/drf-yasg/swagger-ui-dist/ >/dev/null
rm -f swagger-ui.js
rm -f package.json .npmignore README.md rm -f package.json .npmignore README.md
rm -f index.html *.map rm -f index.html *.map
popd >/dev/null popd >/dev/null