Skip to content

Commit

Permalink
Merge pull request #12102 from dushaniw/patch-master-fix-swagger
Browse files Browse the repository at this point in the history
Reflect API security configs in devportal OpenAPI 3.0 | Swagger 2.0 definition
  • Loading branch information
dushaniw authored Sep 8, 2023
2 parents 105f161 + d3c1ee4 commit cd9b2ab
Show file tree
Hide file tree
Showing 45 changed files with 6,324 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ public final class APIConstants {
public static final String DEFAULT_API_SECURITY_OAUTH2 = "oauth2";
public static final String API_SECURITY_MUTUAL_SSL = "mutualssl";
public static final String API_SECURITY_BASIC_AUTH = "basic_auth";
public static final String SWAGGER_API_SECURITY_BASIC_AUTH_TYPE = "basic";
public static final String API_SECURITY_API_KEY = "api_key";
public static final String API_SECURITY_MUTUAL_SSL_MANDATORY = "mutualssl_mandatory";
public static final String API_SECURITY_OAUTH_BASIC_AUTH_API_KEY_MANDATORY = "oauth_basic_auth_api_key_mandatory";
Expand Down Expand Up @@ -1576,6 +1577,9 @@ private ConfigParameters() {
public static final String SWAGGER_IS_MISSING_MSG = "swagger is missing";
public static final String OPENAPI_IS_MISSING_MSG = "openapi is missing";
public static final String SWAGGER_X_SCOPES_BINDINGS = "x-scopes-bindings";
public static final String SWAGGER_X_BASIC_AUTH_SCOPES = "x-scopes";
public static final String SWAGGER_X_BASIC_AUTH_RESOURCE_SCOPES = "x-basic-auth-scopes";
public static final String OPENAPI_SECURITY_SCHEMA_KEY = "default";

//swagger v1.2 constants
public static final String SWAGGER_RESOURCES = "resources";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@
import io.swagger.models.RefResponse;
import io.swagger.models.Response;
import io.swagger.models.Scheme;
import io.swagger.models.SecurityRequirement;
import io.swagger.models.Swagger;
import io.swagger.models.auth.ApiKeyAuthDefinition;
import io.swagger.models.auth.BasicAuthDefinition;
import io.swagger.models.auth.In;
import io.swagger.models.auth.OAuth2Definition;
import io.swagger.models.auth.SecuritySchemeDefinition;
import io.swagger.models.parameters.PathParameter;
Expand Down Expand Up @@ -902,25 +904,11 @@ public String getOASVersion(String oasDefinition) {
* @param swaggerData Swagger related data
*/
private void updateSwaggerSecurityDefinition(Swagger swagger, SwaggerData swaggerData, String authUrl) {

OAuth2Definition oAuth2Definition = new OAuth2Definition().implicit(authUrl);
Set<Scope> scopes = swaggerData.getScopes();
if (scopes != null && !scopes.isEmpty()) {
Map<String, String> scopeBindings = new HashMap<>();
for (Scope scope : scopes) {
String description = scope.getDescription() != null ? scope.getDescription() : "";
oAuth2Definition.addScope(scope.getKey(), description);
String roles = (StringUtils.isNotBlank(scope.getRoles())
&& scope.getRoles().trim().split(",").length > 0) ? scope.getRoles() : StringUtils.EMPTY;
scopeBindings.put(scope.getKey(), roles);
}
oAuth2Definition.setVendorExtension(APIConstants.SWAGGER_X_SCOPES_BINDINGS, scopeBindings);
}
OASParserUtil.setScopesFromAPIToSecurityScheme(swaggerData, oAuth2Definition);
swagger.addSecurityDefinition(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY, oAuth2Definition);
if (swagger.getSecurity() == null) {
SecurityRequirement securityRequirement = new SecurityRequirement();
securityRequirement.setRequirements(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY, new ArrayList<String>());
swagger.addSecurity(securityRequirement);
}
OASParserUtil.addSecurityRequirementToSwagger(swagger, SWAGGER_APIM_DEFAULT_SECURITY);
}

/**
Expand Down Expand Up @@ -1338,10 +1326,103 @@ private String updateSwaggerSecurityDefinitionForStore(Swagger swagger, SwaggerD
} else {
authUrl = (hostsWithSchemes.get(APIConstants.HTTP_PROTOCOL)).concat("/authorize");
}
updateSwaggerSecurityDefinition(swagger, swaggerData, authUrl);
updateSwaggerSecurityDefinitionForStore(swagger, swaggerData, authUrl);
return getSwaggerJsonString(swagger);
}


/**
* Update Swagger security definition for dev portal only.
*
* @param swagger Swagger
* @param swaggerData SwaggerData
* @param authUrl Authorization URL
*/
private void updateSwaggerSecurityDefinitionForStore(Swagger swagger, SwaggerData swaggerData, String authUrl) {

// Get the security defined for the current API.
List<String> secList = swaggerData.getSecurity() != null ? Arrays.asList(swaggerData.getSecurity().split(","))
: new ArrayList<>();
if (secList.isEmpty() || secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)) {
// Add oauth to global security requirement to the OAS definition.
if (log.isDebugEnabled()) {
log.debug("Updating the Swagger definition with default oauth2 security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
OASParserUtil.addSecurityRequirementToSwagger(swagger, APIConstants.SWAGGER_APIM_DEFAULT_SECURITY);
OAuth2Definition oAuth2Definition = new OAuth2Definition().implicit(authUrl);
OASParserUtil.setScopesFromAPIToSecurityScheme(swaggerData, oAuth2Definition);
swagger.addSecurityDefinition(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY, oAuth2Definition);
}
// If the Basic Auth security is in API, add basic security to the OAS definition.
if (secList.contains(APIConstants.API_SECURITY_BASIC_AUTH)) {
if (log.isDebugEnabled()) {
log.debug("Updating the Swagger definition with basic_auth security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
OASParserUtil.addSecurityRequirementToSwagger(swagger, APIConstants.API_SECURITY_BASIC_AUTH);
BasicAuthDefinition basicAuthDefinition = new BasicAuthDefinition();
OASParserUtil.setScopesFromAPIToSecurityScheme(swaggerData, basicAuthDefinition);
swagger.addSecurityDefinition(APIConstants.API_SECURITY_BASIC_AUTH, basicAuthDefinition);
}
if (secList.contains(APIConstants.API_SECURITY_API_KEY)) {
if (log.isDebugEnabled()) {
log.debug("Updating the Swagger definition with api_key security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
OASParserUtil.addSecurityRequirementToSwagger(swagger, APIConstants.API_SECURITY_API_KEY);
ApiKeyAuthDefinition apiKeyAuthDefinition = new ApiKeyAuthDefinition();
apiKeyAuthDefinition.setName(APIConstants.API_KEY_HEADER_QUERY_PARAM);
apiKeyAuthDefinition.setIn(In.HEADER);
swagger.addSecurityDefinition(APIConstants.API_SECURITY_API_KEY, apiKeyAuthDefinition);
}
// Add security requirements with scopes to the operations in OAS definition.
for (Map.Entry<String, Path> pathEntry : swagger.getPaths().entrySet()) {
for (Operation operation : pathEntry.getValue().getOperations()) {
List<Map<String, List<String>>> oldSecList = operation.getSecurity();
if (oldSecList == null) {
oldSecList = new ArrayList<>();
}
// Get scopes from default oauth2 security of each resource.
List<String> operationScopes = oldSecList.stream()
.filter(security -> security.containsKey(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY))
.findFirst()
.map(security -> security.get(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY))
.orElse(new ArrayList<>());
// Add operation level security for basic_auth and api_key.
OASParserUtil.addSwaggerBasicAuthResourceScopesFromAPI(operationScopes, secList, operation);
OASParserUtil.addSwaggerOperationSecurityReqFromAPI(oldSecList, secList,
APIConstants.API_SECURITY_BASIC_AUTH, new ArrayList<>());
OASParserUtil.addSwaggerOperationSecurityReqFromAPI(oldSecList, secList,
APIConstants.API_SECURITY_API_KEY, new ArrayList<>());
if (!secList.isEmpty() && !secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)
&& operation.getSecurity() != null) {
// If oauth2 is not set for the API, remove oauth security scheme from resource level if exists.
operation.setSecurity(operation.getSecurity().stream()
.filter(securityRequirement -> !securityRequirement
.containsKey(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY))
.collect(Collectors.toList()));
}
}
}
if (!secList.isEmpty() && !secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)) {
// If oauth2 is not set for the API, remove oauth security scheme from global level if exists.
if (log.isDebugEnabled()) {
log.debug("Removing default oauth2 security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion() + " from Swagger definition");
}
if (swagger.getSecurityDefinitions() != null) {
swagger.getSecurityDefinitions().remove(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY);
}
if (swagger.getSecurity() != null) {
swagger.setSecurity(swagger.getSecurity().stream().filter(
securityRequirement -> !securityRequirement.getRequirements()
.containsKey(APIConstants.SWAGGER_APIM_DEFAULT_SECURITY))
.collect(Collectors.toList()));
}
}
}

@Override
public String getOASDefinitionWithTierContentAwareProperty(String oasDefinition,
List<String> contentAwareTiersList, String apiLevelTier) throws APIManagementException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1301,10 +1301,133 @@ private String updateSwaggerSecurityDefinitionForStore(OpenAPI openAPI, SwaggerD
} else {
authUrl = (hostsWithSchemes.get(APIConstants.HTTP_PROTOCOL)).concat("/authorize");
}
updateSwaggerSecurityDefinition(openAPI, swaggerData, authUrl);
updateSwaggerSecurityDefinitionForStore(openAPI, swaggerData, authUrl);
return Json.pretty(openAPI);
}


/**
* Update Swagger security definition for dev portal only.
*
* @param openAPI OpenAPI
* @param swaggerData SwaggerData
* @param authUrl Authorization URL
*/
private void updateSwaggerSecurityDefinitionForStore(OpenAPI openAPI, SwaggerData swaggerData, String authUrl) {

if (openAPI.getComponents() == null) openAPI.setComponents(new Components());
// Get the security defined for the current API.
List<String> secList = swaggerData.getSecurity() != null ? Arrays.asList(swaggerData.getSecurity().split(","))
: new ArrayList<>();
// Get the security schemes defined in the OAS definition.
Map<String, SecurityScheme> securitySchemes = openAPI.getComponents().getSecuritySchemes();
if (securitySchemes == null) {
// If no security schemes defined, create a new map.
securitySchemes = new HashMap<>();
openAPI.getComponents().setSecuritySchemes(securitySchemes);
}
List<SecurityRequirement> security = new ArrayList<>(); // Override with new global security requirements.
openAPI.setSecurity(security);
// If the security in API is empty or default oauth, add oauth2 security to the OAS definition.
if (secList.isEmpty() || secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)) {
if (log.isDebugEnabled()) {
log.debug("Updating the OAS definition with default oauth2 security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
// Add oauth to global security requirement to the OAS definition.
OASParserUtil.addSecurityRequirementToSwagger(openAPI, OPENAPI_SECURITY_SCHEMA_KEY);
// If default oauth type security scheme in the OAS definition, add it.
SecurityScheme securityScheme = securitySchemes.computeIfAbsent(OPENAPI_SECURITY_SCHEMA_KEY,
key -> {
SecurityScheme newOAuthScheme = new SecurityScheme();
newOAuthScheme.setType(SecurityScheme.Type.OAUTH2);
return newOAuthScheme;
});
if (securityScheme.getFlows() == null) { // If no flows defined, create a new one.
securityScheme.setFlows(new OAuthFlows());
}
OAuthFlow oAuthFlow = securityScheme.getFlows().getImplicit();
if (oAuthFlow == null) { // If no implicit flow defined, create a new one.
oAuthFlow = new OAuthFlow();
securityScheme.getFlows().setImplicit(oAuthFlow);
}
// rewrite the authorization url if the authorization url is not empty.
oAuthFlow.setAuthorizationUrl(authUrl);
// Set the scopes defined in the API to the OAS definition.
OASParserUtil.setScopesFromAPIToSecurityScheme(swaggerData, securityScheme);
}
// If the Basic Auth security is in API, add basic security to the OAS definition.
if (secList.contains(APIConstants.API_SECURITY_BASIC_AUTH)) {
if (log.isDebugEnabled()) {
log.debug("Updating the OAS definition with basic_auth security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
SecurityScheme securityScheme = securitySchemes.computeIfAbsent(APIConstants.API_SECURITY_BASIC_AUTH,
key -> {
SecurityScheme scheme = new SecurityScheme();
scheme.setType(SecurityScheme.Type.HTTP);
scheme.setScheme(APIConstants.SWAGGER_API_SECURITY_BASIC_AUTH_TYPE);
return scheme;
});
// Set the scopes defined in the API to the OAS definition.
OASParserUtil.setScopesFromAPIToSecurityScheme(swaggerData, securityScheme);
// Add global basic security requirement to the OAS definition.
OASParserUtil.addSecurityRequirementToSwagger(openAPI, APIConstants.API_SECURITY_BASIC_AUTH);
}
if (secList.contains(APIConstants.API_SECURITY_API_KEY)) {
if (log.isDebugEnabled()) {
log.debug("Updating the OAS definition with api_key security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion());
}
securitySchemes.computeIfAbsent(APIConstants.API_SECURITY_API_KEY,
key -> {
SecurityScheme scheme = new SecurityScheme();
scheme.setType(SecurityScheme.Type.APIKEY);
scheme.setIn(SecurityScheme.In.HEADER);
scheme.setName(APIConstants.API_KEY_HEADER_QUERY_PARAM);
return scheme;
});
// Add global api key security requirement to the OAS definition.
OASParserUtil.addSecurityRequirementToSwagger(openAPI, APIConstants.API_SECURITY_API_KEY);
}
// Add requirement with scopes to the operations in OAS definition.
for (Map.Entry<String, PathItem> pathEntry : openAPI.getPaths().entrySet()) {
for (Operation operation : pathEntry.getValue().readOperations()) {
List<SecurityRequirement> oldSecList = operation.getSecurity();
if (oldSecList == null) {
oldSecList = new ArrayList<>();
}
List<String> operationScopes = oldSecList.stream()
.filter(securityRequirement -> securityRequirement.containsKey(OPENAPI_SECURITY_SCHEMA_KEY))
.findFirst()
.map(securityRequirement -> securityRequirement.get(OPENAPI_SECURITY_SCHEMA_KEY))
.orElse(new ArrayList<>());
// Add operation level security for basic_auth and api_key.
OASParserUtil.addOASBasicAuthResourceScopesFromAPI(operationScopes, secList, operation);
OASParserUtil.addOASOperationSecurityReqFromAPI(oldSecList, secList,
APIConstants.API_SECURITY_BASIC_AUTH, new ArrayList<>());
OASParserUtil.addOASOperationSecurityReqFromAPI(oldSecList, secList, APIConstants.API_SECURITY_API_KEY,
new ArrayList<>());
if (!secList.isEmpty() && !secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)
&& operation.getSecurity() != null) {
// If oauth2 is not set for the API, remove oauth security scheme from resource level if exists.
operation.setSecurity(operation.getSecurity().stream()
.filter(securityRequirement -> !securityRequirement
.containsKey(OPENAPI_SECURITY_SCHEMA_KEY))
.collect(Collectors.toList()));
}
}
}
if (!secList.isEmpty() && !secList.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)) {
if (log.isDebugEnabled()) {
log.debug("Removing default oauth2 security of API: " + swaggerData.getTitle()
+ " Version: " + swaggerData.getVersion() + " from OAS definition");
}
// Remove oauth security scheme from global level and resource level if exists
securitySchemes.remove(OPENAPI_SECURITY_SCHEMA_KEY);
}
}

/**
* Update OAS definition with GW endpoints
*
Expand Down
Loading

0 comments on commit cd9b2ab

Please sign in to comment.