Skip to content

Commit

Permalink
feature: Docker OAuth block support (via #4987)
Browse files Browse the repository at this point in the history
* add `onFound` callback to schemas
* add warning to method docs (for #4957)
* implement Docker OAuth2 init block support
* update docs
* add OAUTH_SCOPE_SEPARATOR
* drop OAuth env from Dockerfile and run script
* don't indent the first oauth block line
* drop unused `dedent` import
* touch up warning message
* add more test cases
* return an empty block if no OAuth content is generated
* fix broken doc line
  • Loading branch information
shockey committed Nov 1, 2018
1 parent 31a8b13 commit c6eb8ed
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 163 deletions.
5 changes: 0 additions & 5 deletions Dockerfile
Expand Up @@ -9,11 +9,6 @@ RUN apk add nodejs
LABEL maintainer="fehguy"

ENV API_KEY "**None**"
ENV OAUTH_CLIENT_ID "**None**"
ENV OAUTH_CLIENT_SECRET "**None**"
ENV OAUTH_REALM "**None**"
ENV OAUTH_APP_NAME "**None**"
ENV OAUTH_ADDITIONAL_PARAMS "**None**"
ENV SWAGGER_JSON "/app/swagger.json"
ENV PORT 8080
ENV BASE_URL ""
Expand Down
13 changes: 13 additions & 0 deletions docker/configurator/helpers.js
@@ -0,0 +1,13 @@
module.exports.indent = function indent(str, len, fromLine = 0) {

return str
.split("\n")
.map((line, i) => {
if (i + 1 >= fromLine) {
return `${Array(len + 1).join(" ")}${line}`
} else {
return line
}
})
.join("\n")
}
21 changes: 5 additions & 16 deletions docker/configurator/index.js
Expand Up @@ -2,7 +2,8 @@ const fs = require("fs")
const path = require("path")

const translator = require("./translator")
const configSchema = require("./variables")
const oauthBlockBuilder = require("./oauth")
const indent = require("./helpers").indent

const START_MARKER = "// Begin Swagger UI call region"
const END_MARKER = "// End Swagger UI call region"
Expand All @@ -22,19 +23,7 @@ fs.writeFileSync(targetPath, `${beforeStartMarkerContent}
const ui = SwaggerUIBundle({
${indent(translator(process.env, { injectBaseConfig: true }), 8, 2)}
})
${indent(oauthBlockBuilder(process.env), 6, 2)}
${END_MARKER}
${afterEndMarkerContent}`)

function indent(str, len, fromLine) {

return str
.split("\n")
.map((line, i) => {
if(i + 1 >= fromLine) {
return `${Array(len + 1).join(" ")}${line}`
} else {
return line
}
})
.join("\n")
}
${afterEndMarkerContent}`)
43 changes: 43 additions & 0 deletions docker/configurator/oauth.js
@@ -0,0 +1,43 @@
const translator = require("./translator")
const indent = require("./helpers").indent

const oauthBlockSchema = {
OAUTH_CLIENT_ID: {
type: "string",
name: "clientId"
},
OAUTH_CLIENT_SECRET: {
type: "string",
name: "clientSecret",
onFound: () => console.warn("Swagger UI warning: don't use `OAUTH_CLIENT_SECRET` in production!")
},
OAUTH_REALM: {
type: "string",
name: "realm"
},
OAUTH_APP_NAME: {
type: "string",
name: "appName"
},
OAUTH_SCOPE_SEPARATOR: {
type: "string",
name: "scopeSeparator"
},
OAUTH_ADDITIONAL_PARAMS: {
type: "object",
name: "additionalQueryStringParams"
}
}

module.exports = function oauthBlockBuilder(env) {
const translatorResult = translator(env, { schema: oauthBlockSchema })

if(translatorResult) {
return (
`ui.initOAuth({
${indent(translatorResult, 2)}
})`)
}

return ``
}
4 changes: 4 additions & 0 deletions docker/configurator/translator.js
Expand Up @@ -55,6 +55,10 @@ function objectToKeyValueString(env, { injectBaseConfig = false, schema = config

if(!varSchema) return

if(varSchema.onFound) {
varSchema.onFound()
}

const storageContents = valueStorage[varSchema.name]

if(storageContents) {
Expand Down
7 changes: 0 additions & 7 deletions docker/run.sh
Expand Up @@ -28,13 +28,6 @@ if [ "${BASE_URL}" ]; then
fi

replace_in_index myApiKeyXXXX123456789 $API_KEY
replace_or_delete_in_index your-client-id $OAUTH_CLIENT_ID
replace_or_delete_in_index your-client-secret-if-required $OAUTH_CLIENT_SECRET
replace_or_delete_in_index your-realms $OAUTH_REALM
replace_or_delete_in_index your-app-name $OAUTH_APP_NAME
if [ "$OAUTH_ADDITIONAL_PARAMS" != "**None**" ]; then
replace_in_index "additionalQueryStringParams: {}" "additionalQueryStringParams: {$OAUTH_ADDITIONAL_PARAMS}"
fi

if [[ -f $SWAGGER_JSON ]]; then
cp -s $SWAGGER_JSON $NGINX_ROOT
Expand Down
6 changes: 4 additions & 2 deletions docs/usage/configuration.md
Expand Up @@ -81,9 +81,11 @@ Parameter name | Docker variable | Description

### Instance methods

Parameter name | Docker variable | Description
**💡 Take note! These are methods, not parameters**.

Method name | Docker variable | Description
--- | --- | -----
<a name="initOAuth"></a>`initOAuth` | _Unavailable_ | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
<a name="initOAuth"></a>`initOAuth` | [_See `oauth2.md`_](./oauth2.md) | `(configObj) => void`. Provide Swagger-UI with information about your OAuth server - see the OAuth2 documentation for more information.
<a name="preauthorizeBasic"></a>`preauthorizeBasic` | _Unavailable_ | `(authDefinitionKey, username, password) => action`. Programmatically set values for a Basic authorization scheme.
<a name="preauthorizeApiKey"></a>`preauthorizeApiKey` | _Unavailable_ | `(authDefinitionKey, apiKeyValue) => action`. Programmatically set values for an API key authorization scheme.

Expand Down
18 changes: 9 additions & 9 deletions docs/usage/oauth2.md
@@ -1,15 +1,15 @@
# OAuth2 configuration
You can configure OAuth2 authorization by calling the `initOAuth` method.

Config Name | Description
--- | ---
clientId | Default clientId. MUST be a string
clientSecret | **🚨 Never use this parameter in your production environemnt. It exposes cruicial security information. This feature is intended for dev/test environments only. 🚨** <br>Default clientSecret. MUST be a string
realm | realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl`. 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
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 base64encode(client_id + client_secret)`). The default is `false`
Property name | Docker variable | Description
--- | --- | ------
clientId | `OAUTH_CLIENT_ID` | Default clientId. MUST be a string
clientSecret | `OAUTH_CLIENT_SECRET` | **🚨 Never use this parameter in your production environemnt. It exposes cruicial security information. This feature is intended for dev/test environments only. 🚨** <br>Default clientSecret. MUST be a string
realm | `OAUTH_REALM` |realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl`. MUST be a string
appName | `OAUTH_APP_NAME` |application name, displayed in authorization popup. MUST be a string
scopeSeparator | `OAUTH_SCOPE_SEPARATOR` |scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | `OAUTH_ADDITIONAL_PARAMS` |Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
useBasicAuthenticationWithAccessCodeGrant | _Unavailable_ |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({...})
Expand Down
58 changes: 58 additions & 0 deletions test/docker/oauth.js
@@ -0,0 +1,58 @@
const expect = require("expect")
const oauthBlockBuilder = require("../../docker/configurator/oauth")
const dedent = require("dedent")

describe("docker: env translator - oauth block", function() {
it("should omit the block if there are no valid keys", function () {
const input = {}

expect(oauthBlockBuilder(input)).toEqual(``)
})
it("should omit the block if there are no valid keys", function () {
const input = {
NOT_A_VALID_KEY: "asdf1234"
}

expect(oauthBlockBuilder(input)).toEqual(``)
})
it("should generate a block from empty values", function() {
const input = {
OAUTH_CLIENT_ID: ``,
OAUTH_CLIENT_SECRET: ``,
OAUTH_REALM: ``,
OAUTH_APP_NAME: ``,
OAUTH_SCOPE_SEPARATOR: "",
OAUTH_ADDITIONAL_PARAMS: ``,
}

expect(oauthBlockBuilder(input)).toEqual(dedent(`
ui.initOAuth({
clientId: "",
clientSecret: "",
realm: "",
appName: "",
scopeSeparator: "",
additionalQueryStringParams: undefined,
})`))
})
it("should generate a full block", function() {
const input = {
OAUTH_CLIENT_ID: `myId`,
OAUTH_CLIENT_SECRET: `mySecret`,
OAUTH_REALM: `myRealm`,
OAUTH_APP_NAME: `myAppName`,
OAUTH_SCOPE_SEPARATOR: "%21",
OAUTH_ADDITIONAL_PARAMS: `{ "a": 1234, "b": "stuff" }`,
}

expect(oauthBlockBuilder(input)).toEqual(dedent(`
ui.initOAuth({
clientId: "myId",
clientSecret: "mySecret",
realm: "myRealm",
appName: "myAppName",
scopeSeparator: "%21",
additionalQueryStringParams: { "a": 1234, "b": "stuff" },
})`))
})
})

0 comments on commit c6eb8ed

Please sign in to comment.