From b35fd4752e616e9a7b1e00c0a6492ef040a2dd54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Gorej?= Date: Wed, 6 Sep 2023 16:51:42 +0200 Subject: [PATCH] fix: provide fixes for mutualTLS (#9195) --- .../oas31/auth-extensions/wrap-selectors.js | 103 ++++++---- src/core/plugins/oas31/components/auths.jsx | 189 +++++++++++------- .../oas31/components/mutual-tls-auth.jsx | 33 +++ src/core/plugins/oas31/index.js | 12 +- .../oas31/wrap-components/auth-item.jsx | 47 ++--- .../plugins/oas31/wrap-components/auths.jsx | 12 +- 6 files changed, 244 insertions(+), 152 deletions(-) create mode 100644 src/core/plugins/oas31/components/mutual-tls-auth.jsx diff --git a/src/core/plugins/oas31/auth-extensions/wrap-selectors.js b/src/core/plugins/oas31/auth-extensions/wrap-selectors.js index 45f9cb90cdc..103208698db 100644 --- a/src/core/plugins/oas31/auth-extensions/wrap-selectors.js +++ b/src/core/plugins/oas31/auth-extensions/wrap-selectors.js @@ -1,7 +1,6 @@ /** * @prettier */ - import { fromJS, Map, List } from "immutable" import { createOnlyOAS31SelectorWrapper } from "../fn" @@ -12,73 +11,89 @@ export const selectLicenseUrl = createOnlyOAS31SelectorWrapper( ) export const definitionsToAuthorize = createOnlyOAS31SelectorWrapper( - () => (oriSelector, system) => { + () => (oriSelector, system) => { const definitions = system.getSystem().specSelectors.securityDefinitions() let list = List() - if(!definitions) { - return list - } + if (!definitions) { + return list + } - definitions.entrySeq().forEach( ([ defName, definition ]) => { - const type = definition.get("type") - if(type === "oauth2") { - definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => { + definitions.entrySeq().forEach(([defName, definition]) => { + const type = definition.get("type") + if (type === "oauth2") { + definition + .get("flows") + .entrySeq() + .forEach(([flowKey, flowVal]) => { let translatedDef = fromJS({ flow: flowKey, authorizationUrl: flowVal.get("authorizationUrl"), tokenUrl: flowVal.get("tokenUrl"), scopes: flowVal.get("scopes"), type: definition.get("type"), - description: definition.get("description") + description: definition.get("description"), }) const w1 = new Map({ [defName]: translatedDef.filter((v) => { // filter out unset values, sometimes `authorizationUrl` // and `tokenUrl` come out as `undefined` in the data return v !== undefined - }) + }), }) list = list.push(w1) }) - } - if(type === "http" || type === "apiKey") { - list = list.push(new Map({ - [defName]: definition - })) - } - if(type === "openIdConnect" && definition.get("openIdConnectData")) { - let oidcData = definition.get("openIdConnectData") - let grants = oidcData.get("grant_types_supported") || ["authorization_code", "implicit"] - grants.forEach((grant) => { - // Convert from OIDC list of scopes to the OAS-style map with empty descriptions - let translatedScopes = oidcData.get("scopes_supported") && - oidcData.get("scopes_supported").reduce((acc, cur) => acc.set(cur, ""), new Map()) + } + if (type === "http" || type === "apiKey") { + list = list.push( + new Map({ + [defName]: definition, + }) + ) + } + if (type === "openIdConnect" && definition.get("openIdConnectData")) { + let oidcData = definition.get("openIdConnectData") + let grants = oidcData.get("grant_types_supported") || [ + "authorization_code", + "implicit", + ] + grants.forEach((grant) => { + // Convert from OIDC list of scopes to the OAS-style map with empty descriptions + let translatedScopes = + oidcData.get("scopes_supported") && + oidcData + .get("scopes_supported") + .reduce((acc, cur) => acc.set(cur, ""), new Map()) - let translatedDef = fromJS({ - flow: grant, - authorizationUrl: oidcData.get("authorization_endpoint"), - tokenUrl: oidcData.get("token_endpoint"), - scopes: translatedScopes, - type: "oauth2", - openIdConnectUrl: definition.get("openIdConnectUrl") - }) + let translatedDef = fromJS({ + flow: grant, + authorizationUrl: oidcData.get("authorization_endpoint"), + tokenUrl: oidcData.get("token_endpoint"), + scopes: translatedScopes, + type: "oauth2", + openIdConnectUrl: definition.get("openIdConnectUrl"), + }) - list = list.push(new Map({ + list = list.push( + new Map({ [defName]: translatedDef.filter((v) => { // filter out unset values, sometimes `authorizationUrl` // and `tokenUrl` come out as `undefined` in the data return v !== undefined - }) - })) + }), + }) + ) + }) + } + + if (type === "mutualTLS") { + list = list.push( + new Map({ + [defName]: definition, }) - } - - if(type === "mutualTLS") { - list = list.push(new Map({ - [defName]: definition - })) - } - }) - return list + ) + } }) + return list + } +) diff --git a/src/core/plugins/oas31/components/auths.jsx b/src/core/plugins/oas31/components/auths.jsx index c82ee89fb6e..6a3ffc44444 100644 --- a/src/core/plugins/oas31/components/auths.jsx +++ b/src/core/plugins/oas31/components/auths.jsx @@ -1,15 +1,18 @@ +/** + * @prettier + */ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" -export default class Auths extends React.Component { +class Auths extends React.Component { static propTypes = { definitions: ImPropTypes.iterable.isRequired, getComponent: PropTypes.func.isRequired, authSelectors: PropTypes.object.isRequired, authActions: PropTypes.object.isRequired, errSelectors: PropTypes.object.isRequired, - specSelectors: PropTypes.object.isRequired + specSelectors: PropTypes.object.isRequired, } constructor(props, context) { @@ -24,30 +27,34 @@ export default class Auths extends React.Component { this.setState({ [name]: auth }) } - submitAuth =(e) => { + submitAuth = (e) => { e.preventDefault() let { authActions } = this.props authActions.authorizeWithPersistOption(this.state) } - logoutClick =(e) => { + logoutClick = (e) => { e.preventDefault() let { authActions, definitions } = this.props - let auths = definitions.map( (val, key) => { - return key - }).toArray() - - this.setState(auths.reduce((prev, auth) => { - prev[auth] = "" - return prev - }, {})) + let auths = definitions + .map((val, key) => { + return key + }) + .toArray() + + this.setState( + auths.reduce((prev, auth) => { + prev[auth] = "" + return prev + }, {}) + ) authActions.logoutWithPersistOption(auths) } - close =(e) => { + close = (e) => { e.preventDefault() let { authActions } = this.props @@ -59,83 +66,115 @@ export default class Auths extends React.Component { const AuthItem = getComponent("AuthItem") const Oauth2 = getComponent("oauth2", true) const Button = getComponent("Button") - let authorized = authSelectors.authorized() - let authorizedAuth = definitions.filter( (definition, key) => { + const authorized = authSelectors.authorized() + const authorizedAuth = definitions.filter((definition, key) => { return !!authorized.get(key) }) - - let nonOauthDefinitions = definitions.filter( schema => schema.get("type") !== "oauth2" && schema.get("type") !== "mutualTLS") - let oauthDefinitions = definitions.filter( schema => schema.get("type") === "oauth2") - let mutualTLSDefinitions = definitions.filter( schema => schema.get("type") === "mutualTLS") + const nonOauthDefinitions = definitions.filter( + (schema) => + schema.get("type") !== "oauth2" && schema.get("type") !== "mutualTLS" + ) + const oauthDefinitions = definitions.filter( + (schema) => schema.get("type") === "oauth2" + ) + const mutualTLSDefinitions = definitions.filter( + (schema) => schema.get("type") === "mutualTLS" + ) return (
- { - !!nonOauthDefinitions.size &&
- { - nonOauthDefinitions.map( (schema, name) => { - return 0 && ( + + {nonOauthDefinitions + .map((schema, name) => { + return ( + - }).toArray() - } + ) + }) + .toArray()}
- { - nonOauthDefinitions.size === authorizedAuth.size ? - : - } - + {nonOauthDefinitions.size === authorizedAuth.size ? ( + + ) : ( + + )} +
- } - - { - oauthDefinitions && oauthDefinitions.size ?
-
-

Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.

-

API requires the following scopes. Select which ones you want to grant to Swagger UI.

+ )} + + {oauthDefinitions.size > 0 ? ( +
+
+

+ Scopes are used to grant an application different levels of + access to data on behalf of the end user. Each API may declare + one or more scopes. +

+

+ API requires the following scopes. Select which ones you want to + grant to Swagger UI. +

+
+ {definitions + .filter((schema) => schema.get("type") === "oauth2") + .map((schema, name) => { + return ( +
+ +
+ ) + }) + .toArray()}
- { - definitions.filter( schema => schema.get("type") === "oauth2") - .map( (schema, name) =>{ - return (
- -
) - } - ).toArray() - } -
: null - } - { - !!mutualTLSDefinitions.size &&
- { - mutualTLSDefinitions.map( (schema, name) => { - return 0 && ( +
+ {mutualTLSDefinitions + .map((schema, name) => { + return ( + - }).toArray() - } + ) + }) + .toArray()}
- } - + )}
) } - } +export default Auths diff --git a/src/core/plugins/oas31/components/mutual-tls-auth.jsx b/src/core/plugins/oas31/components/mutual-tls-auth.jsx new file mode 100644 index 00000000000..a601108262a --- /dev/null +++ b/src/core/plugins/oas31/components/mutual-tls-auth.jsx @@ -0,0 +1,33 @@ +/** + * @prettier + */ +import React from "react" +import PropTypes from "prop-types" + +const MutualTLSAuth = ({ schema, getComponent }) => { + const JumpToPath = getComponent("JumpToPath", true) + + return ( +
+

+ {schema.name} (mutualTLS){" "} + +

+

+ Mutual TLS is required by this API/Operation. Certificates are managed + via your Operating System and/or your browser. +

+

{schema.description}

+
+ ) +} + +MutualTLSAuth.propTypes = { + schema: PropTypes.shape({ + name: PropTypes.string, + description: PropTypes.string, + }).isRequired, + getComponent: PropTypes.func.isRequired, +} + +export default MutualTLSAuth diff --git a/src/core/plugins/oas31/index.js b/src/core/plugins/oas31/index.js index c11d3fc8308..f18d90c7469 100644 --- a/src/core/plugins/oas31/index.js +++ b/src/core/plugins/oas31/index.js @@ -9,6 +9,8 @@ import JsonSchemaDialect from "./components/json-schema-dialect" import VersionPragmaFilter from "./components/version-pragma-filter" import Model from "./components/model/model" import Models from "./components/models/models" +import MutualTLSAuth from "./components/mutual-tls-auth" +import Auths from "./components/auths" import LicenseWrapper from "./wrap-components/license" import ContactWrapper from "./wrap-components/contact" import InfoWrapper from "./wrap-components/info" @@ -52,7 +54,7 @@ import { isOAS3 as isOAS3SelectorWrapper, selectLicenseUrl as selectLicenseUrlWrapper, } from "./spec-extensions/wrap-selectors" -import { definitionsToAuthorize as definitionsToAuthorizeWrapper} from "./auth-extensions/wrap-selectors" +import { definitionsToAuthorize as definitionsToAuthorizeWrapper } from "./auth-extensions/wrap-selectors" import { selectLicenseUrl as selectOAS31LicenseUrl } from "./selectors" import JSONSchema202012KeywordExample from "./json-schema-2020-12-extensions/components/keywords/Example" import JSONSchema202012KeywordXml from "./json-schema-2020-12-extensions/components/keywords/Xml" @@ -62,7 +64,6 @@ import JSONSchema202012KeywordDescriptionWrapper from "./json-schema-2020-12-ext import JSONSchema202012KeywordDefaultWrapper from "./json-schema-2020-12-extensions/wrap-components/keywords/Default" import JSONSchema202012KeywordPropertiesWrapper from "./json-schema-2020-12-extensions/wrap-components/keywords/Properties" import afterLoad from "./after-load" -import Auths from "./components/auths" const OAS31Plugin = ({ fn }) => { const createSystemSelector = fn.createSystemSelector || createSystemSelectorFn @@ -78,17 +79,18 @@ const OAS31Plugin = ({ fn }) => { components: { Webhooks, JsonSchemaDialect, + MutualTLSAuth, OAS31Info: Info, OAS31License: License, OAS31Contact: Contact, OAS31VersionPragmaFilter: VersionPragmaFilter, OAS31Model: Model, OAS31Models: Models, + OAS31Auths: Auths, JSONSchema202012KeywordExample, JSONSchema202012KeywordXml, JSONSchema202012KeywordDiscriminator, JSONSchema202012KeywordExternalDocs, - OAS31Auths: Auths }, wrapComponents: { InfoContainer: InfoWrapper, @@ -98,7 +100,7 @@ const OAS31Plugin = ({ fn }) => { Model: ModelWrapper, Models: ModelsWrapper, AuthItem: AuthItemWrapper, - Auths: AuthsWrapper, + auths: AuthsWrapper, JSONSchema202012KeywordDescription: JSONSchema202012KeywordDescriptionWrapper, JSONSchema202012KeywordDefault: JSONSchema202012KeywordDefaultWrapper, @@ -108,7 +110,7 @@ const OAS31Plugin = ({ fn }) => { statePlugins: { auth: { wrapSelectors: { - definitionsToAuthorize: definitionsToAuthorizeWrapper + definitionsToAuthorize: definitionsToAuthorizeWrapper, }, }, spec: { diff --git a/src/core/plugins/oas31/wrap-components/auth-item.jsx b/src/core/plugins/oas31/wrap-components/auth-item.jsx index b091df05dde..419a781f9ac 100644 --- a/src/core/plugins/oas31/wrap-components/auth-item.jsx +++ b/src/core/plugins/oas31/wrap-components/auth-item.jsx @@ -5,30 +5,31 @@ import React from "react" import { createOnlyOAS31ComponentWrapper } from "../fn" -const AuthItem = createOnlyOAS31ComponentWrapper(({ originalComponent: Ori, ...props }) => { - const { name, errSelectors, authorized, getComponent, onChange, schema } = props - const HttpAuth = getComponent("HttpAuth") - const JumpToPath = getComponent("JumpToPath", true) - const type = schema.get("type") - const description = schema.get("description") - -if(type === "http") { - return - } else if (type === "mutualTLS") { - return
-

{name} (mutualTLS)

-

Mutual TLS is required by this API/Operation. Try-it-out in SwaggerUI doesn't currently support client-side certificates

-

{description}

-
- } else { +const AuthItem = createOnlyOAS31ComponentWrapper( + ({ originalComponent: Ori, ...props }) => { + const { name, errSelectors, authorized } = props + const { getComponent, onChange, schema } = props + const HttpAuth = getComponent("HttpAuth") + const MutualTLSAuth = getComponent("MutualTLSAuth", true) + const type = schema.get("type") + + if (type === "http") { + return ( + + ) + } else if (type === "mutualTLS") { + return + } + return } -}) +) export default AuthItem diff --git a/src/core/plugins/oas31/wrap-components/auths.jsx b/src/core/plugins/oas31/wrap-components/auths.jsx index 68ec438e075..308cb2ff342 100644 --- a/src/core/plugins/oas31/wrap-components/auths.jsx +++ b/src/core/plugins/oas31/wrap-components/auths.jsx @@ -5,11 +5,13 @@ import React from "react" import { createOnlyOAS31ComponentWrapper } from "../fn" -const AuthsWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => { - const system = getSystem() - const OAS31Auths = system.getComponent("OAS31Auths", true) +const AuthsWrapper = createOnlyOAS31ComponentWrapper( + ({ getSystem, ...props }) => { + const system = getSystem() + const OAS31Auths = system.getComponent("OAS31Auths", true) - return -}) + return + } +) export default AuthsWrapper