feat: preauthorization (#4339)

* feat: swagger 2.0 preauthorization methods
* tests: add cases for oas3 preauthorization
* docs: add new preauth docs; touch up existing auth docs
* tests: add `rootInject` tests
* docs: remove unfinished sentence
This commit is contained in:
kyle
2018-03-16 22:25:04 -07:00
committed by GitHub
parent 624a81201f
commit 245428e7cd
5 changed files with 241 additions and 4 deletions

View File

@@ -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 `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 `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.

View File

@@ -1,7 +1,5 @@
# OAuth2 configuration # OAuth2 configuration
You can configure OAuth2 authorization by calling `initOAuth` method with passed configs under the instance of `SwaggerUIBundle` You can configure OAuth2 authorization by calling the `initOAuth` method.
default `client_id` and `client_secret`, `realm`, an application name `appName`, `scopeSeparator`, `additionalQueryStringParams`,
`useBasicAuthenticationWithAccessCodeGrant`.
Config Name | Description 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 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 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 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 ```javascript
const ui = SwaggerUI({...}) const ui = SwaggerUI({...})

View File

@@ -8,6 +8,8 @@ export default function() {
afterLoad(system) { afterLoad(system) {
this.rootInjects = this.rootInjects || {} this.rootInjects = this.rootInjects || {}
this.rootInjects.initOAuth = system.authActions.configureAuth this.rootInjects.initOAuth = system.authActions.configureAuth
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
}, },
statePlugins: { statePlugins: {
auth: { 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()
}
})
}

View File

@@ -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)
})
})
})

View File

@@ -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() { describe("error catching", function() {
it("should encapsulate thrown errors in an afterLoad method", function() { it("should encapsulate thrown errors in an afterLoad method", function() {
// Given // Given