diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index de7e9242..61c530ef 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -78,3 +78,9 @@ Parameter Name | Description --- | --- `modelPropertyMacro` | `Function`. Function to set default values to each property in model. Accepts one argument modelPropertyMacro(property), property is immutable `parameterMacro` | `Function`. Function to set default value to parameters. Accepts two arguments parameterMacro(operation, parameter). Operation and parameter are objects passed for context, both remain immutable + +### Instance methods + +`initOAuth` | Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information. +`preauthorizeBasic` | Programmatically set values for a Basic authorization scheme. +`preauthorizeApiKey` | Programmatically set values for an API key authorization scheme. diff --git a/docs/usage/oauth2.md b/docs/usage/oauth2.md index b47c097c..0a727f93 100644 --- a/docs/usage/oauth2.md +++ b/docs/usage/oauth2.md @@ -1,7 +1,5 @@ # OAuth2 configuration -You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle` -default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`, -`useBasicAuthenticationWithAccessCodeGrant`. +You can configure OAuth2 authorization by calling the `initOAuth` method. Config Name | Description --- | --- @@ -11,7 +9,7 @@ realm | realm query parameter (for oauth1) added to `authorizationUrl` and `toke appName | application name, displayed in authorization popup. MUST be a string scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object -useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false` +useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encode(client_id + client_secret)`). The default is `false` ```javascript const ui = SwaggerUI({...}) diff --git a/src/core/plugins/auth/index.js b/src/core/plugins/auth/index.js index 3ac5e1ff..04e6d138 100644 --- a/src/core/plugins/auth/index.js +++ b/src/core/plugins/auth/index.js @@ -8,6 +8,8 @@ export default function() { afterLoad(system) { this.rootInjects = this.rootInjects || {} this.rootInjects.initOAuth = system.authActions.configureAuth + this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system) + this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system) }, statePlugins: { auth: { @@ -21,3 +23,50 @@ export default function() { } } } + +export function preauthorizeBasic(system, key, username, password) { + const { + authActions: { authorize }, + specSelectors: { specJson, isOAS3 } + } = system + + const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"] + + const schema = specJson().getIn([...definitionBase, key]) + + if(!schema) { + return null + } + + return authorize({ + [key]: { + value: { + username, + password, + }, + schema: schema.toJS() + } + }) +} + +export function preauthorizeApiKey(system, key, value) { + const { + authActions: { authorize }, + specSelectors: { specJson, isOAS3 } + } = system + + const definitionBase = isOAS3() ? ["components", "securitySchemes"] : ["securityDefinitions"] + + const schema = specJson().getIn([...definitionBase, key]) + + if(!schema) { + return null + } + + return authorize({ + [key]: { + value, + schema: schema.toJS() + } + }) +} diff --git a/test/core/plugins/auth/preauthorize.js b/test/core/plugins/auth/preauthorize.js new file mode 100644 index 00000000..3149c8d7 --- /dev/null +++ b/test/core/plugins/auth/preauthorize.js @@ -0,0 +1,154 @@ +/* eslint-env mocha */ +import expect from "expect" +import { fromJS } from "immutable" +import { preauthorizeBasic, preauthorizeApiKey } from "corePlugins/auth" +import { authorize } from "corePlugins/auth/actions" + +const S2_SYSTEM = { + authActions: { + authorize + }, + specSelectors: { + isOAS3: () => false, + specJson: () => { + return fromJS({ + swagger: "2.0", + securityDefinitions: { + "APIKeyHeader": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key" + }, + "basicAuth": { + "type": "basic" + } + } + }) + } + } +} + +const OAI3_SYSTEM = { + authActions: { + authorize + }, + specSelectors: { + isOAS3: () => true, + specJson: () => { + return fromJS({ + openapi: "3.0.0", + components: { + securitySchemes: { + basicAuth: { + type: "http", + scheme: "basic" + }, + APIKeyHeader: { + type: "apiKey", + in: "header", + name: "X-API-Key" + } + } + } + }) + } + } +} + +describe("auth plugin - preauthorizers", () => { + describe("preauthorizeBasic", () => { + it("should return a valid authorize action in Swagger 2", () => { + const res = preauthorizeBasic(S2_SYSTEM, "basicAuth", "user", "pass") + + expect(res).toEqual({ + type: "authorize", + payload: { + basicAuth: { + schema: { + type: "basic" + }, + value: { + username: "user", + password: "pass" + } + } + } + }) + }) + it("should return a valid authorize action in OpenAPI 3", () => { + const res = preauthorizeBasic(OAI3_SYSTEM, "basicAuth", "user", "pass") + + expect(res).toEqual({ + type: "authorize", + payload: { + basicAuth: { + schema: { + type: "http", + scheme: "basic" + }, + value: { + username: "user", + password: "pass" + } + } + } + }) + }) + it("should return null when the authorization name is invalid in Swagger 2", () => { + const res = preauthorizeBasic(S2_SYSTEM, "fakeBasicAuth", "user", "pass") + + expect(res).toEqual(null) + }) + it("should return null when the authorization name is invalid in OpenAPI 3", () => { + const res = preauthorizeBasic(OAI3_SYSTEM, "fakeBasicAuth", "user", "pass") + + expect(res).toEqual(null) + }) + }) + describe("preauthorizeApiKey", () => { + it("should return a valid authorize action in Swagger 2", () => { + const res = preauthorizeApiKey(S2_SYSTEM, "APIKeyHeader", "Asdf1234") + + expect(res).toEqual({ + type: "authorize", + payload: { + APIKeyHeader: { + schema: { + type: "apiKey", + name: "X-API-Key", + "in": "header" + }, + value: "Asdf1234" + } + } + }) + }) + it("should return a valid authorize action in OpenAPI 3", () => { + const res = preauthorizeApiKey(OAI3_SYSTEM, "APIKeyHeader", "Asdf1234") + + expect(res).toEqual({ + type: "authorize", + payload: { + APIKeyHeader: { + schema: { + type: "apiKey", + "in": "header", + name: "X-API-Key" + }, + value: "Asdf1234" + } + } + }) + }) + it("should return null when the authorization name is invalid in Swagger 2", () => { + const res = preauthorizeApiKey(S2_SYSTEM, "FakeAPIKeyHeader", "Asdf1234") + + expect(res).toEqual(null) + }) + it("should return null when the authorization name is invalid in OpenAPI 3", () => { + const res = preauthorizeApiKey(OAI3_SYSTEM, "FakeAPIKeyHeader", "Asdf1234") + + expect(res).toEqual(null) + }) + }) +}) diff --git a/test/core/system/system.js b/test/core/system/system.js index 69e18488..5e53e597 100644 --- a/test/core/system/system.js +++ b/test/core/system/system.js @@ -701,6 +701,36 @@ describe("bound system", function(){ }) }) + describe("rootInjects", function() { + it("should attach a rootInject function as an instance method", function() { + // This is the same thing as the `afterLoad` tests, but is here for posterity + + // Given + const system = new System({ + plugins: [ + { + afterLoad(system) { + this.rootInjects.wow = system.dogeSelectors.wow + }, + statePlugins: { + doge: { + selectors: { + wow: () => (system) => { + return "so selective" + } + } + } + } + } + ] + }) + + // When + var res = system.getSystem().wow() + expect(res).toEqual("so selective") + }) + }) + describe("error catching", function() { it("should encapsulate thrown errors in an afterLoad method", function() { // Given