From fc7ac74867aaf2bd18d07b5fd1ef9d668ba32fb6 Mon Sep 17 00:00:00 2001 From: Calvin Bascom Date: Fri, 21 Mar 2025 01:19:05 -0400 Subject: [PATCH] Add support for normalizing bearerAuth The openapi 2.0 spec did not support bearer authentication but it was added in openapi 3.0. In order to support client generation that includes support for bearerAuth, this change adds a new feature to the OpenapiNormalizer so that it can be configured to look for a specific securityDefinition name and convert it to bearerAuth. --- docs/customization.md | 24 ++++++++++ .../codegen/OpenAPINormalizer.java | 45 ++++++++++++++++++- .../codegen/OpenAPINormalizerTest.java | 23 ++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/docs/customization.md b/docs/customization.md index 72e037e9a90a..c4c36bf782fd 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -646,3 +646,27 @@ Example: ``` java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_1/java/petstore.yaml -o /tmp/java-okhttp/ --openapi-normalizer FIX_DUPLICATED_OPERATIONID=true ``` + +- `SET_BEARER_AUTH_FOR_NAME`: When set to the name of an openapi 2.0 securityDefinition, that securityDefinition will be converted to the openapi 3.0 bearerAuth securityScheme. + +Example: +``` +java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/2_0/globalSecurity.json -o /tmp/java-okhttp/ --openapi-normalizer SET_BEARER_AUTH_FOR_NAME=api_key +``` +Transforms this securityDefinition: +``` + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + }, + }, +``` +Into this securityScheme: +``` + securitySchemes: + api_key: + scheme: bearer + type: http +``` diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index d58b6a9bcf08..f75cb1fbef3c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; @@ -101,6 +102,10 @@ public class OpenAPINormalizer { String fixDuplicatedOperationId; HashSet operationIdSet = new HashSet<>(); + // when set to true, if a securityScheme is found with the specified name, it will be converted to bearerAuth + final String SET_BEARER_AUTH_FOR_NAME = "SET_BEARER_AUTH_FOR_NAME"; + String bearerAuthSecuritySchemeName; + // when set to true, auto fix integer with maximum value 4294967295 (2^32-1) or long with 18446744073709551615 (2^64-1) // by adding x-unsigned to the schema final String ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE = "ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE"; @@ -167,6 +172,7 @@ public OpenAPINormalizer(OpenAPI openAPI, Map inputRules) { ruleNames.add(SET_TAGS_TO_OPERATIONID); ruleNames.add(SET_TAGS_TO_VENDOR_EXTENSION); ruleNames.add(FIX_DUPLICATED_OPERATIONID); + ruleNames.add(SET_BEARER_AUTH_FOR_NAME); ruleNames.add(ADD_UNSIGNED_TO_INTEGER_WITH_INVALID_MAX_VALUE); ruleNames.add(REFACTOR_ALLOF_WITH_PROPERTIES_ONLY); ruleNames.add(NORMALIZE_31SPEC); @@ -301,6 +307,11 @@ public void processRules(Map inputRules) { LOGGER.error("SET_PRIMITIVE_TYPES_TO_NULLABLE rule must be in the form of `string|integer|number|boolean`, e.g. `string`, `integer|number`: {}", inputRules.get(SET_PRIMITIVE_TYPES_TO_NULLABLE)); } } + + bearerAuthSecuritySchemeName = inputRules.get(SET_BEARER_AUTH_FOR_NAME); + if (bearerAuthSecuritySchemeName != null) { + rules.put(SET_BEARER_AUTH_FOR_NAME, true); + } } /** @@ -322,6 +333,7 @@ void normalize() { normalizeInfo(); normalizePaths(); + normalizeComponentsSecuritySchemes(); normalizeComponentsSchemas(); normalizeComponentsResponses(); } @@ -547,6 +559,36 @@ private void normalizeHeaders(Map headers) { } } + /** + * Normalizes securitySchemes in components + */ + private void normalizeComponentsSecuritySchemes() { + if (StringUtils.isEmpty(bearerAuthSecuritySchemeName)) { + return; + } + + Map schemes = openAPI.getComponents().getSecuritySchemes(); + if (schemes == null) { + return; + } + + for (String schemeKey : schemes.keySet()) { + if (schemeKey.equals(bearerAuthSecuritySchemeName)) { + SecurityScheme scheme = schemes.get(schemeKey); + scheme.setType(SecurityScheme.Type.HTTP); + scheme.setScheme("bearer"); + scheme.setIn(null); + scheme.setName(null); + scheme.setBearerFormat(null); + scheme.setFlows(null); + scheme.setOpenIdConnectUrl(null); + scheme.setExtensions(null); + scheme.set$ref(null); + schemes.put(schemeKey, scheme); + } + } + } + /** * Normalizes schemas in components */ @@ -560,7 +602,7 @@ private void normalizeComponentsSchemas() { for (String schemaName : schemaNames) { Schema schema = schemas.get(schemaName); if (schema == null) { - LOGGER.warn("{} not fount found in openapi/components/schemas.", schemaName); + LOGGER.warn("{} not found in openapi/components/schemas.", schemaName); } else { // remove x-internal if needed if (schema.getExtensions() != null && getRule(REMOVE_X_INTERNAL)) { @@ -1053,7 +1095,6 @@ private void processFixDuplicatedOperationId(Operation operation) { } } - /** * If the schema contains anyOf/oneOf and properties, remove oneOf/anyOf as these serve as rules to * ensure inter-dependency between properties. It's a workaround as such validation is not supported at the moment. diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index 34b06b9819d6..85104bc2ea0d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.*; import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.openapitools.codegen.utils.ModelUtils; import org.testng.annotations.Test; @@ -942,4 +943,26 @@ public void testOpenAPINormalizerComponentsResponses31Spec() { assertEquals(((Schema) apiResponse2.getContent().get("application/json").getSchema().getProperties().get("uuid")).getType(), "integer"); assertEquals(((Schema) apiResponse2.getContent().get("application/json").getSchema().getProperties().get("label")).getType(), "string"); } + + @Test + public void testOpenAPINormalizerBearerAuthSpec() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/2_0/globalSecurity.json"); + SecurityScheme scheme = openAPI.getComponents().getSecuritySchemes().get("api_key"); + assertEquals(scheme.getType(), SecurityScheme.Type.APIKEY); + assertEquals(scheme.getScheme(), null); + assertEquals(scheme.getName(), "api_key"); + assertEquals(scheme.getIn(), SecurityScheme.In.HEADER); + + Map inputRules = Map.of( + "SET_BEARER_AUTH_FOR_NAME", "api_key" + ); + OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, inputRules); + openAPINormalizer.normalize(); + + SecurityScheme scheme2 = openAPI.getComponents().getSecuritySchemes().get("api_key"); + assertEquals(scheme.getType(), SecurityScheme.Type.HTTP); + assertEquals(scheme.getScheme(), "bearer"); + assertEquals(scheme.getName(), null); + assertEquals(scheme.getIn(), null); + } }