diff --git a/pom.xml b/pom.xml
index 069307ba..4e868162 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,11 +30,11 @@
io.gravitee
gravitee-parent
- 15
+ 16
- 1.15.0
+ 1.16.0-SNAPSHOT
1.5.0
1.1.0
1.15.0
diff --git a/src/main/java/io/gravitee/policy/oauth2/Oauth2Policy.java b/src/main/java/io/gravitee/policy/oauth2/Oauth2Policy.java
index f3b5e1dd..2570ab7f 100644
--- a/src/main/java/io/gravitee/policy/oauth2/Oauth2Policy.java
+++ b/src/main/java/io/gravitee/policy/oauth2/Oauth2Policy.java
@@ -40,6 +40,7 @@
import java.util.*;
import static io.gravitee.gateway.api.ExecutionContext.ATTR_USER;
+import static io.gravitee.gateway.api.ExecutionContext.ATTR_USER_ROLES;
/**
* @author David BRASSELY (david.brassely at graviteesource.com)
@@ -132,6 +133,7 @@ Handler handleResponse(PolicyChain policyChain, Request request,
final OAuth2Resource oauth2 = executionContext.getComponent(ResourceManager.class).getResource(
oAuth2PolicyConfiguration.getOauthResource(), OAuth2Resource.class);
+
// Extract user
final String user = oauthResponseNode.path(oauth2.getUserClaim() == null ?
OAUTH_PAYLOAD_SUB_NODE : oauth2.getUserClaim()).asText();
@@ -139,10 +141,16 @@ Handler handleResponse(PolicyChain policyChain, Request request,
executionContext.setAttribute(ATTR_USER, user);
}
+ // Extract scopes from introspection response
+ List scopes = extractScopes(oauthResponseNode, oauth2.getScopeSeparator());
+ executionContext.setAttribute(ATTR_USER_ROLES, scopes);
+
// Check required scopes to access the resource
if (oAuth2PolicyConfiguration.isCheckRequiredScopes()) {
- if (! hasRequiredScopes(oauthResponseNode, oAuth2PolicyConfiguration.getRequiredScopes(),
- oauth2.getScopeSeparator(), oAuth2PolicyConfiguration.isModeStrict())) {
+ if (! hasRequiredScopes(
+ scopes,
+ oAuth2PolicyConfiguration.getRequiredScopes(),
+ oAuth2PolicyConfiguration.isModeStrict())) {
sendError(response, policyChain, "insufficient_scope",
"The request requires higher privileges than provided by the access token.");
return;
@@ -196,15 +204,11 @@ private JsonNode readPayload(String oauthPayload) {
}
}
- static boolean hasRequiredScopes(JsonNode oauthResponseNode, List requiredScopes, String scopeSeparator,
- final boolean modeStrict) {
- if (requiredScopes == null || requiredScopes.isEmpty()) {
- return true;
- }
-
+ static List extractScopes(JsonNode oauthResponseNode, String scopeSeparator) {
JsonNode scopesNode = oauthResponseNode.path(OAUTH_PAYLOAD_SCOPE_NODE);
List scopes;
+
if (scopesNode instanceof ArrayNode) {
Iterator scopeIterator = scopesNode.elements();
scopes = new ArrayList<>(scopesNode.size());
@@ -214,10 +218,23 @@ static boolean hasRequiredScopes(JsonNode oauthResponseNode, List requir
scopes = Arrays.asList(scopesNode.asText().split(scopeSeparator));
}
+ return scopes;
+ }
+
+ static boolean hasRequiredScopes(Collection tokenScopes, List requiredScopes,
+ final boolean modeStrict) {
+ if (requiredScopes == null || requiredScopes.isEmpty()) {
+ return true;
+ }
+
+ if (tokenScopes == null || tokenScopes.isEmpty()) {
+ return false;
+ }
+
if (modeStrict) {
- return scopes.containsAll(requiredScopes) && requiredScopes.containsAll(scopes);
+ return tokenScopes.containsAll(requiredScopes) && requiredScopes.containsAll(tokenScopes);
} else {
- return scopes.stream().anyMatch(requiredScopes::contains);
+ return tokenScopes.stream().anyMatch(requiredScopes::contains);
}
}
}
diff --git a/src/test/java/io/gravitee/policy/oauth2/OAuth2PolicyTest.java b/src/test/java/io/gravitee/policy/oauth2/OAuth2PolicyTest.java
index 2caa1d4e..117cf587 100644
--- a/src/test/java/io/gravitee/policy/oauth2/OAuth2PolicyTest.java
+++ b/src/test/java/io/gravitee/policy/oauth2/OAuth2PolicyTest.java
@@ -31,6 +31,7 @@
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -38,14 +39,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.UUID;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import java.util.*;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
@@ -191,70 +189,80 @@ public void shouldCallOAuthResourceAndHandleResult() throws Exception {
@Test
public void shouldValidScopes_noRequiredScopes() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response01.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, null, DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, null, false);
Assert.assertTrue(valid);
}
@Test
public void shouldNotValidScopes_emptyOAuthResponse() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response01.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), false);
Assert.assertFalse(valid);
}
@Test
public void shouldValidScopes_emptyOAuthResponse() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response02.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), false);
Assert.assertTrue(valid);
}
@Test
public void shouldValidScopes_stringOfScopes() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response04.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), false);
Assert.assertTrue(valid);
}
@Test
public void shouldValidScopes_stringOfScopes_customSeparator() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response06.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), ",", false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, ",");
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), false);
Assert.assertTrue(valid);
}
@Test
public void shouldValidScopes_arrayOfScope() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response05.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), false);
Assert.assertTrue(valid);
}
@Test
public void shouldValidScopes_arrayOfScopes() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response07.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Arrays.asList("read", "write"), DEFAULT_OAUTH_SCOPE_SEPARATOR, false);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Arrays.asList("read", "write"), false);
Assert.assertTrue(valid);
}
@Test
public void shouldValidScopes_arrayOfScopes_strict() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response05.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Arrays.asList("read", "write", "admin"), DEFAULT_OAUTH_SCOPE_SEPARATOR, true);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Arrays.asList("read", "write", "admin"), true);
Assert.assertTrue(valid);
}
@Test
public void shouldInvalidScopes_arrayOfScope_strict() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response05.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Collections.singletonList("read"), DEFAULT_OAUTH_SCOPE_SEPARATOR, true);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Collections.singletonList("read"), true);
Assert.assertFalse(valid);
}
@Test
public void shouldInvalidScopes_arrayOfScopes_strict() throws IOException {
JsonNode jsonNode = readJsonResource("/io/gravitee/policy/oauth2/oauth2-response05.json");
- boolean valid = Oauth2Policy.hasRequiredScopes(jsonNode, Arrays.asList("read", "write"), DEFAULT_OAUTH_SCOPE_SEPARATOR, true);
+ Collection scopes = Oauth2Policy.extractScopes(jsonNode, DEFAULT_OAUTH_SCOPE_SEPARATOR);
+ boolean valid = Oauth2Policy.hasRequiredScopes(scopes, Arrays.asList("read", "write"), true);
Assert.assertFalse(valid);
}
@@ -312,6 +320,7 @@ public void shouldFail_goodIntrospection_noClientId() throws IOException {
when(oAuth2PolicyConfiguration.getOauthResource()).thenReturn("oauth2");
when(mockExecutionContext.getComponent(ResourceManager.class)).thenReturn(resourceManager);
when(resourceManager.getResource(oAuth2PolicyConfiguration.getOauthResource(), OAuth2Resource.class)).thenReturn(customOAuth2Resource);
+ when(customOAuth2Resource.getScopeSeparator()).thenReturn(DEFAULT_OAUTH_SCOPE_SEPARATOR);
Handler handler = policy.handleResponse(mockPolicychain, mockRequest, mockResponse, mockExecutionContext);
String payload = readResource("/io/gravitee/policy/oauth2/oauth2-response03.json");
@@ -329,6 +338,8 @@ public void shouldValidate_goodIntrospection_withClientId() throws IOException {
Oauth2Policy policy = new Oauth2Policy(oAuth2PolicyConfiguration);
when(mockExecutionContext.getComponent(ResourceManager.class)).thenReturn(resourceManager);
when(resourceManager.getResource(oAuth2PolicyConfiguration.getOauthResource(), OAuth2Resource.class)).thenReturn(customOAuth2Resource);
+ when(customOAuth2Resource.getScopeSeparator()).thenReturn(DEFAULT_OAUTH_SCOPE_SEPARATOR);
+
Handler handler = policy.handleResponse(mockPolicychain, mockRequest, mockResponse, mockExecutionContext);
String payload = readResource("/io/gravitee/policy/oauth2/oauth2-response04.json");
@@ -336,6 +347,15 @@ public void shouldValidate_goodIntrospection_withClientId() throws IOException {
verify(mockExecutionContext).setAttribute(Oauth2Policy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");
verify(mockExecutionContext).setAttribute(Oauth2Policy.CONTEXT_ATTRIBUTE_OAUTH_PAYLOAD, payload);
+ verify(mockExecutionContext).setAttribute(eq(ExecutionContext.ATTR_USER_ROLES), argThat(new ArgumentMatcher>() {
+ @Override
+ public boolean matches(List scopes) {
+ return
+ scopes.get(0).equals("read") &&
+ scopes.get(1).equals("write") &&
+ scopes.get(2).equals("admin");
+ }
+ }));
verify(mockPolicychain).doNext(mockRequest, mockResponse);
}