Skip to content

Commit

Permalink
Fixing ScopeNotFound issue when API defaultSecurityScheme has other o…
Browse files Browse the repository at this point in the history
…authFlows (wso2#6453)

* Fixing ScopeNotFound issue when API default securityScheme has other oauthFlows

* Adding unit tests

* Fixing formatting issues
  • Loading branch information
HiranyaKavishani committed Apr 19, 2024
1 parent a1815eb commit 3063e44
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ public abstract String getOASDefinitionWithTierContentAwareProperty(String oasDe
public abstract String processOtherSchemeScopes(String resourceConfigsJSON)
throws APIManagementException;

/**
* This method returns OAS definition of default security scheme which support multiple oauth flows.
* @param resourceConfigsJSON
* @return
* @throws APIManagementException
*/
public abstract String processDefaultSchemeScopesOfMultipleOauthFlows(String resourceConfigsJSON)
throws APIManagementException;

/**
* This method returns OAS definition which replaced X-WSO2-throttling-tier extension comes from
* mgw with X-throttling-tier extensions in OAS file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1904,6 +1904,11 @@ public String processOtherSchemeScopes(String resourceConfigsJSON) throws APIMan
return null;
}

@Override
public String processDefaultSchemeScopesOfMultipleOauthFlows(String resourceConfigsJSON) throws APIManagementException {
return null;
}

@Override
public API setExtensionsToAPI(String swaggerContent, API api) throws APIManagementException {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,13 @@ public String processOtherSchemeScopes(String swaggerContent) throws APIManageme
return swaggerContent;
}

@Override
public String processDefaultSchemeScopesOfMultipleOauthFlows(String swaggerContent) throws APIManagementException {
// This method is used to inject scopes of multiple oauth flows to the default scheme,
// But OAS2 does not support multiple oauth flows.
return null;
}

/**
* This method returns swagger definition which replaced X-WSO2-throttling-tier extension comes from
* mgw with X-throttling-tier extensions in swagger file(Swagger version 2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import org.wso2.carbon.apimgt.api.model.URITemplate;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.utils.APIUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -1072,6 +1073,60 @@ private Set<Scope> getScopesFromExtensions(OpenAPI openAPI) throws APIManagement
return scopeList;
}

private Set<Scope> getScopesFromDefaultOAuthFlows(OpenAPI openAPI) {
HashSet<Scope> scopeSet = new HashSet<>();

Map<String, SecurityScheme> securitySchemes;
SecurityScheme defaultScheme;
OAuthFlows oAuthFlows;

if (openAPI.getComponents() != null && (securitySchemes = openAPI.getComponents()
.getSecuritySchemes()) != null && (defaultScheme = securitySchemes.get(
OPENAPI_SECURITY_SCHEMA_KEY)) != null && (oAuthFlows = defaultScheme.getFlows()) != null) {

OAuthFlow oAuthFlow = oAuthFlows.getImplicit();
OAuthFlow authorizationCodeFlow = oAuthFlows.getAuthorizationCode();
OAuthFlow passwordFlow = oAuthFlows.getPassword();
OAuthFlow clientCredentialsFlow = oAuthFlows.getClientCredentials();

if (oAuthFlow != null && oAuthFlow.getScopes() != null) {
extractScopesFromOauthFlow(scopeSet, oAuthFlow);
}

if (authorizationCodeFlow != null && authorizationCodeFlow.getScopes() != null) {
extractScopesFromOauthFlow(scopeSet, authorizationCodeFlow);
}

if (passwordFlow != null) {
extractScopesFromOauthFlow(scopeSet, passwordFlow);
}

if (clientCredentialsFlow != null) {
extractScopesFromOauthFlow(scopeSet, clientCredentialsFlow);
}

}
return scopeSet;
}

private void extractScopesFromOauthFlow(HashSet<Scope> scopeSet, OAuthFlow oAuthFlow) {
for (Map.Entry<String, String> entry : oAuthFlow.getScopes().entrySet()) {
Scope scope = new Scope();
scope.setKey(entry.getKey());
scope.setName(entry.getKey());
scope.setDescription(entry.getValue());
Map<String, String> scopeBindings;
if (oAuthFlow.getExtensions() != null && (scopeBindings = (Map<String, String>) oAuthFlow.getExtensions()
.get(APIConstants.SWAGGER_X_SCOPES_BINDINGS)) != null) {
if (scopeBindings.get(scope.getKey()) != null) {
scope.setRoles(scopeBindings.get(scope.getKey()));
}
}
scopeSet.add(scope);
}
}


/**
* Include Scope details to the definition
*
Expand Down Expand Up @@ -1709,6 +1764,58 @@ public String processOtherSchemeScopes(String swaggerContent) throws APIManageme
return swaggerContent;
}

/**
* This method will inject scopes of multiple oauth flows to default security schemes in the swagger definition
* @param swaggerContent
* @return String
*/
@Override
public String processDefaultSchemeScopesOfMultipleOauthFlows(String swaggerContent) throws APIManagementException {
OpenAPI openAPI = getOpenAPI(swaggerContent);
Set<Scope> scopesFromDefaultOauthFlows = getScopesFromDefaultOAuthFlows(openAPI);

if (!scopesFromDefaultOauthFlows.isEmpty()) {
SecurityScheme defaultScheme = openAPI.getComponents().getSecuritySchemes()
.get(OPENAPI_SECURITY_SCHEMA_KEY);
OAuthFlow oAuthFlow = defaultScheme.getFlows().getImplicit();
if (oAuthFlow == null) {
oAuthFlow = new OAuthFlow();
defaultScheme.getFlows().setImplicit(oAuthFlow);
}
if (oAuthFlow.getAuthorizationUrl() == null) {
oAuthFlow.setAuthorizationUrl(OPENAPI_DEFAULT_AUTHORIZATION_URL);
}

Scopes oas3Scopes = oAuthFlow.getScopes() != null ? oAuthFlow.getScopes() : new Scopes();

Map<String, String> scopeBindings = new HashMap<>();

if (oAuthFlow.getExtensions() != null) {
scopeBindings = oAuthFlow.getExtensions().get(APIConstants.SWAGGER_X_SCOPES_BINDINGS) != null ?
(Map<String, String>) oAuthFlow.getExtensions().get(APIConstants.SWAGGER_X_SCOPES_BINDINGS) :
new HashMap<>();
}

for (Scope scope : scopesFromDefaultOauthFlows) {
if (!oas3Scopes.containsKey(scope.getKey())) {
String description = scope.getDescription() != null ? scope.getDescription() : "";
oas3Scopes.put(scope.getKey(), description);
if (scope.getRoles() != null) {
String roles = (StringUtils.isNotBlank(scope.getRoles()) && scope.getRoles().trim()
.split(",").length > 0) ? scope.getRoles() : StringUtils.EMPTY;
scopeBindings.put(scope.getKey(), roles);
}
}
}

if (!scopeBindings.isEmpty()) {
oAuthFlow.addExtension(APIConstants.SWAGGER_X_SCOPES_BINDINGS, scopeBindings);
}
oAuthFlow.setScopes(oas3Scopes);
}
return Json.pretty(openAPI);
}

/**
* This method returns openAPI definition which replaced X-WSO2-throttling-tier extension comes from
* mgw with X-throttling-tier extensions in openAPI file(openAPI version 3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,9 @@ public static String preProcess(String swaggerContent) throws APIManagementExcep
swaggerContent = apiDefinition.injectMgwThrottlingExtensionsToDefault(swaggerContent);
//Process mgw disable security extension
swaggerContent = apiDefinition.processDisableSecurityExtension(swaggerContent);

swaggerContent = apiDefinition.processDefaultSchemeScopesOfMultipleOauthFlows(swaggerContent);

return apiDefinition.processOtherSchemeScopes(swaggerContent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ public void testGetOASDefinitionWithTierContentAwareProperty() throws Exception
" }\n" +
" }\n" +
"}";

// Content aware tiers TierX and Tier2.
// Test 1: API level content-aware tier: TierX
contentAwareTiersList = new ArrayList<String>();
Expand Down Expand Up @@ -581,4 +581,32 @@ public void testGetOASDefinitionWithTierContentAwareProperty() throws Exception
pathsObj.has(APIConstants.SWAGGER_X_THROTTLING_BANDWIDTH));

}

@Test
public void testSwaggerWithScopesInDefaultSecuritySchemeWithPasswordFlow()
throws IOException, APIManagementException {
String swaggerPath = "definitions" + File.separator + "oas3" + File.separator + "default_password_oauth_flow_scopes.yaml";
String swagger = IOUtils.toString(getClass().getClassLoader().getResourceAsStream(swaggerPath), "UTF-8");

String responsePath = "definitions" + File.separator + "oas3" + File.separator + "default_password_oauth_flow_scopes_response.json";
String expectedSwaggerResponse = IOUtils.toString(getClass().getClassLoader().getResourceAsStream(responsePath),
"UTF-8");

String actualSwaggerResponse = OASParserUtil.preProcess(swagger);
Assert.assertEquals(expectedSwaggerResponse.trim(), actualSwaggerResponse);
}

@Test
public void testSwaggerWithScopesInDefaultSecuritySchemeWithMultipleFlows()
throws IOException, APIManagementException {
String swaggerPath = "definitions" + File.separator + "oas3" + File.separator + "default_multiple_oauth_flows_scopes.yaml";
String swagger = IOUtils.toString(getClass().getClassLoader().getResourceAsStream(swaggerPath), "UTF-8");

String responsePath = "definitions" + File.separator + "oas3" + File.separator + "default_multiple_oauth_flows_scopes_response.json";
String expectedSwaggerResponse = IOUtils.toString(getClass().getClassLoader().getResourceAsStream(responsePath),
"UTF-8");

String actualSwaggerResponse = OASParserUtil.preProcess(swagger);
Assert.assertEquals(expectedSwaggerResponse.trim(), actualSwaggerResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
openapi: 3.0.1
info:
title: TEST_API
version: '1.0.0'
servers:
- url: 'https://localhost:8080/test_backend'
security:
- default: []
paths:
/testpath:
get:
parameters: []
responses:
'200':
description: ok
security:
- OAuth2Security:
- TEST_SCOPE1
x-auth-type: Application & Application User
x-throttling-tier: 10KPerMin
x-wso2-application-security:
security-types:
- oauth2
optional: false
components:
securitySchemes:
default:
type: oauth2
flows:
implicit:
authorizationUrl: 'https://test.com'
scopes:
TEST_SCOPE1: 'HI TEST_SCOPE1'
TEST_SCOPE3: ''
x-scopes-bindings:
TEST_SCOPE1: admin
TEST_SCOPE3: 'internal/publisher'
password:
tokenUrl: 'https://localhost:9443/oauth2/token'
scopes:
TEST_SCOPE1: 'TEST_SCOPE1'
TEST_SCOPE2: 'Hi TEST_SCOPE2'
x-scopes-bindings:
TEST_SCOPE1: 'admin'
clientCredentials:
tokenUrl: https://api.gettyimages.com/oauth2/token/
scopes: {}
authorizationCode:
authorizationUrl: https://slack.com/oauth/authorize
tokenUrl: https://slack.com/api/oauth.access
scopes:
TEST_SCOPE4: 'Hi TEST_SCOPE4'
x-scopes-bindings:
TEST_SCOPE4: 'admin'
x-wso2-basePath: /test_context/0.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"openapi" : "3.0.1",
"info" : {
"title" : "TEST_API",
"version" : "1.0.0"
},
"servers" : [ {
"url" : "https://localhost:8080/test_backend"
} ],
"security" : [ {
"default" : [ ]
} ],
"paths" : {
"/testpath" : {
"get" : {
"parameters" : [ ],
"responses" : {
"200" : {
"description" : "ok"
}
},
"security" : [ {
"OAuth2Security" : [ "TEST_SCOPE1" ]
} ],
"x-auth-type" : "Application & Application User",
"x-throttling-tier" : "10KPerMin",
"x-wso2-application-security" : {
"security-types" : [ "oauth2" ],
"optional" : false
}
}
}
},
"components" : {
"securitySchemes" : {
"default" : {
"type" : "oauth2",
"flows" : {
"implicit" : {
"authorizationUrl" : "https://test.com",
"scopes" : {
"TEST_SCOPE1" : "HI TEST_SCOPE1",
"TEST_SCOPE3" : "",
"TEST_SCOPE2" : "Hi TEST_SCOPE2",
"TEST_SCOPE4" : "Hi TEST_SCOPE4"
},
"x-scopes-bindings" : {
"TEST_SCOPE1" : "admin",
"TEST_SCOPE3" : "internal/publisher",
"TEST_SCOPE4" : "admin"
}
},
"password" : {
"tokenUrl" : "https://localhost:9443/oauth2/token",
"scopes" : {
"TEST_SCOPE1" : "TEST_SCOPE1",
"TEST_SCOPE2" : "Hi TEST_SCOPE2"
},
"x-scopes-bindings" : {
"TEST_SCOPE1" : "admin"
}
},
"clientCredentials" : {
"tokenUrl" : "https://api.gettyimages.com/oauth2/token/",
"scopes" : { }
},
"authorizationCode" : {
"authorizationUrl" : "https://slack.com/oauth/authorize",
"tokenUrl" : "https://slack.com/api/oauth.access",
"scopes" : {
"TEST_SCOPE4" : "Hi TEST_SCOPE4"
},
"x-scopes-bindings" : {
"TEST_SCOPE4" : "admin"
}
}
}
}
}
},
"x-wso2-basePath" : "/test_context/0.1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
openapi: 3.0.1
info:
title: TEST_API
version: '1.0.0'
servers:
- url: 'https://localhost:8080/test_backend'
security:
- default: []
paths:
/testpath:
get:
parameters: []
responses:
'200':
description: ok
security:
- OAuth2Security:
- TEST_SCOPE
x-auth-type: Application & Application User
x-throttling-tier: 10KPerMin
x-wso2-application-security:
security-types:
- oauth2
optional: false
components:
securitySchemes:
default:
type: oauth2
flows:
password:
tokenUrl: 'https://localhost:9443/oauth2/token'
scopes:
TEST_SCOPE: 'TEST_SCOPE'
x-scopes-bindings:
TEST_SCOPE: 'admin'
x-wso2-basePath: /test_context/0.1
Loading

0 comments on commit 3063e44

Please sign in to comment.