Add support for normalizing bearerAuth (#20860)

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.
This commit is contained in:
Calvin Bascom 2025-03-24 07:39:40 -04:00 committed by GitHub
parent 550e31775d
commit cd2fbd6ff4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 90 additions and 2 deletions

View File

@ -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
```

View File

@ -26,6 +26,7 @@ import io.swagger.v3.oas.models.parameters.Parameter;
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<String> 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 class OpenAPINormalizer {
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 class OpenAPINormalizer {
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 @@ public class OpenAPINormalizer {
normalizeInfo();
normalizePaths();
normalizeComponentsSecuritySchemes();
normalizeComponentsSchemas();
normalizeComponentsResponses();
}
@ -547,6 +559,36 @@ public class OpenAPINormalizer {
}
}
/**
* Normalizes securitySchemes in components
*/
private void normalizeComponentsSecuritySchemes() {
if (StringUtils.isEmpty(bearerAuthSecuritySchemeName)) {
return;
}
Map<String, SecurityScheme> 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 @@ public class OpenAPINormalizer {
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 @@ public class OpenAPINormalizer {
}
}
/**
* 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.

View File

@ -20,6 +20,7 @@ import io.swagger.v3.oas.models.OpenAPI;
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 class OpenAPINormalizerTest {
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<String, String> 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);
}
}