From 492893224c8b34d67b82319e1ec601d3b50c1a9b Mon Sep 17 00:00:00 2001 From: Pete Cornish Date: Thu, 2 Dec 2021 11:46:31 +0000 Subject: [PATCH] fix: correctly serialises security schemes in OpenAPI plugin. --- .../service/SpecificationServiceImpl.kt | 8 +- .../plugin/openapi/SecuritySchemesTest.kt | 89 +++++++++ .../security-schemes/imposter-config.yaml | 2 + .../openapi3/security-schemes/spec.yaml | 175 ++++++++++++++++++ 4 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 plugin/openapi/src/test/java/io/gatehill/imposter/plugin/openapi/SecuritySchemesTest.kt create mode 100644 plugin/openapi/src/test/resources/openapi3/security-schemes/imposter-config.yaml create mode 100644 plugin/openapi/src/test/resources/openapi3/security-schemes/spec.yaml diff --git a/plugin/openapi/src/main/java/io/gatehill/imposter/plugin/openapi/service/SpecificationServiceImpl.kt b/plugin/openapi/src/main/java/io/gatehill/imposter/plugin/openapi/service/SpecificationServiceImpl.kt index 7a946b04e..45ebb068b 100644 --- a/plugin/openapi/src/main/java/io/gatehill/imposter/plugin/openapi/service/SpecificationServiceImpl.kt +++ b/plugin/openapi/src/main/java/io/gatehill/imposter/plugin/openapi/service/SpecificationServiceImpl.kt @@ -54,8 +54,8 @@ import io.gatehill.imposter.http.HttpExchange import io.gatehill.imposter.plugin.openapi.config.OpenApiPluginConfig import io.gatehill.imposter.plugin.openapi.config.OpenApiPluginValidationConfig.ValidationIssueBehaviour import io.gatehill.imposter.plugin.openapi.util.ValidationReportUtil -import io.gatehill.imposter.util.MapUtil import io.swagger.models.Scheme +import io.swagger.v3.core.util.Json import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.ExternalDocumentation import io.swagger.v3.oas.models.OpenAPI @@ -98,7 +98,11 @@ class SpecificationServiceImpl @Inject constructor( override fun getCombinedSpecSerialised(allSpecs: List, deriveBasePathFromServerEntries: Boolean): String { return cache.get("combinedSpecSerialised") { try { - return@get MapUtil.JSON_MAPPER.writeValueAsString( + // Use the v3 swagger-core serialiser (io.swagger.v3.core.util.Json) to serialise the spec, + // to benefit from its various mixins that are not present in the io.swagger.util.Json implementation. + // In particular, these mixins correctly serialise extensions and components, like SecurityScheme, + // to their formal values, rather than the Java enum/toString() defaults. + return@get Json.mapper().writeValueAsString( getCombinedSpec(allSpecs, deriveBasePathFromServerEntries) ) } catch (e: JsonGenerationException) { diff --git a/plugin/openapi/src/test/java/io/gatehill/imposter/plugin/openapi/SecuritySchemesTest.kt b/plugin/openapi/src/test/java/io/gatehill/imposter/plugin/openapi/SecuritySchemesTest.kt new file mode 100644 index 000000000..a61ce68cc --- /dev/null +++ b/plugin/openapi/src/test/java/io/gatehill/imposter/plugin/openapi/SecuritySchemesTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016-2021. + * + * This file is part of Imposter. + * + * "Commons Clause" License Condition v1.0 + * + * The Software is provided to you by the Licensor under the License, as + * defined below, subject to the following condition. + * + * Without limiting other conditions in the License, the grant of rights + * under the License will not include, and the License does not grant to + * you, the right to Sell the Software. + * + * For purposes of the foregoing, "Sell" means practicing any or all of + * the rights granted to you under the License to provide to third parties, + * for a fee or other consideration (including without limitation fees for + * hosting or consulting/support services related to the Software), a + * product or service whose value derives, entirely or substantially, from + * the functionality of the Software. Any license notice or attribution + * required by the License must also include this Commons Clause License + * Condition notice. + * + * Software: Imposter + * + * License: GNU Lesser General Public License version 3 + * + * Licensor: Peter Cornish + * + * Imposter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Imposter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Imposter. If not, see . + */ +package io.gatehill.imposter.plugin.openapi + +import com.jayway.restassured.RestAssured +import com.jayway.restassured.http.ContentType +import io.gatehill.imposter.server.BaseVerticleTest +import io.gatehill.imposter.util.HttpUtil +import io.vertx.ext.unit.TestContext +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.junit.Before +import org.junit.Test + +/** + * Tests that security schemes are serialised correctly. + * + * @author Pete Cornish + */ +class SecuritySchemesTest : BaseVerticleTest() { + override val pluginClass = OpenApiPluginImpl::class.java + + @Before + @Throws(Exception::class) + override fun setUp(testContext: TestContext) { + super.setUp(testContext) + RestAssured.baseURI = "http://$host:$listenPort" + } + + override val testConfigDirs = listOf( + "/openapi3/security-schemes" + ) + + /** + * Expects that securityScheme type is serialised correctly. + */ + @Test + fun testExampleRefReturned() { + RestAssured.given() + .log().ifValidationFails() + .accept(ContentType.JSON) + .`when`()[OpenApiPluginImpl.COMBINED_SPECIFICATION_PATH] + .then() + .log().ifValidationFails() + .statusCode(HttpUtil.HTTP_OK) + .body("components.securitySchemes.httpBasic", notNullValue()) + .body("components.securitySchemes.httpBasic.type", equalTo("http")) + } +} diff --git a/plugin/openapi/src/test/resources/openapi3/security-schemes/imposter-config.yaml b/plugin/openapi/src/test/resources/openapi3/security-schemes/imposter-config.yaml new file mode 100644 index 000000000..82850f83c --- /dev/null +++ b/plugin/openapi/src/test/resources/openapi3/security-schemes/imposter-config.yaml @@ -0,0 +1,2 @@ +plugin: openapi +specFile: spec.yaml diff --git a/plugin/openapi/src/test/resources/openapi3/security-schemes/spec.yaml b/plugin/openapi/src/test/resources/openapi3/security-schemes/spec.yaml new file mode 100644 index 000000000..469c4a1a7 --- /dev/null +++ b/plugin/openapi/src/test/resources/openapi3/security-schemes/spec.yaml @@ -0,0 +1,175 @@ +openapi: "3.0.0" +info: + title: Simple API overview + version: 2.0.0 +paths: + /: + get: + operationId: listVersionsv2 + summary: List API versions + responses: + '200': + description: |- + 200 response + content: + application/json: + examples: + foo: + value: + { + "versions": [ + { + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "id": "v2.0", + "links": [ + { + "href": "http://127.0.0.1:8774/v2/", + "rel": "self" + } + ] + }, + { + "status": "EXPERIMENTAL", + "updated": "2013-07-23T11:33:21Z", + "id": "v3.0", + "links": [ + { + "href": "http://127.0.0.1:8774/v3/", + "rel": "self" + } + ] + } + ] + } + '300': + description: |- + 300 response + content: + application/json: + examples: + foo: + value: | + { + "versions": [ + { + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "id": "v2.0", + "links": [ + { + "href": "http://127.0.0.1:8774/v2/", + "rel": "self" + } + ] + }, + { + "status": "EXPERIMENTAL", + "updated": "2013-07-23T11:33:21Z", + "id": "v3.0", + "links": [ + { + "href": "http://127.0.0.1:8774/v3/", + "rel": "self" + } + ] + } + ] + } + /v2: + get: + operationId: getVersionDetailsv2 + summary: Show API version details + responses: + '200': + description: |- + 200 response + content: + application/json: + examples: + foo: + value: + { + "version": { + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml;version=2" + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2" + } + ], + "id": "v2.0", + "links": [ + { + "href": "http://127.0.0.1:8774/v2/", + "rel": "self" + }, + { + "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", + "type": "application/pdf", + "rel": "describedby" + }, + { + "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", + "type": "application/vnd.sun.wadl+xml", + "rel": "describedby" + }, + { + "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", + "type": "application/vnd.sun.wadl+xml", + "rel": "describedby" + } + ] + } + } + '203': + description: |- + 203 response + content: + application/json: + examples: + foo: + value: + { + "version": { + "status": "CURRENT", + "updated": "2011-01-21T11:33:21Z", + "media-types": [ + { + "base": "application/xml", + "type": "application/vnd.openstack.compute+xml;version=2" + }, + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2" + } + ], + "id": "v2.0", + "links": [ + { + "href": "http://23.253.228.211:8774/v2/", + "rel": "self" + }, + { + "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", + "type": "application/pdf", + "rel": "describedby" + }, + { + "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", + "type": "application/vnd.sun.wadl+xml", + "rel": "describedby" + } + ] + } + } +components: + securitySchemes: + httpBasic: + type: "http" + scheme: "basic"