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:
@@ -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.
|
||||
|
||||
@@ -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({...})
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
154
test/core/plugins/auth/preauthorize.js
Normal file
154
test/core/plugins/auth/preauthorize.js
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user