Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing ScopeNotFound issue when API defaultSecurityScheme has other authFlows #12509

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1529,6 +1529,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 @@ -1089,6 +1090,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 @@ -1744,6 +1799,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 @@ -1697,6 +1697,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 @@ -528,7 +528,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 @@ -582,4 +582,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
Loading