From 65d7b00f4d61b4da50918f9869991f6e71d368fa Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 10 Mar 2015 12:38:37 -0400 Subject: [PATCH] added uma-processing of scopes to introspection results --- .../service/IntrospectionResultAssembler.java | 7 +++- .../DefaultIntrospectionResultAssembler.java | 14 +++++-- .../oauth2/web/IntrospectionEndpoint.java | 38 +++++++++++++------ ...stDefaultIntrospectionResultAssembler.java | 24 +++++++++--- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/IntrospectionResultAssembler.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/IntrospectionResultAssembler.java index 42c186dac..c4bc519b5 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/IntrospectionResultAssembler.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/IntrospectionResultAssembler.java @@ -18,6 +18,7 @@ package org.mitre.oauth2.service; import java.text.SimpleDateFormat; import java.util.Map; +import java.util.Set; import javax.swing.text.DateFormatter; @@ -46,17 +47,19 @@ public interface IntrospectionResultAssembler { * * @param accessToken the access token * @param userInfo the user info + * @param authScopes TODO * @return the token introspection result */ - Map assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo); + Map assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo, Set authScopes); /** * Assemble a token introspection result from the given refresh token and user info. * * @param refreshToken the refresh token * @param userInfo the user info + * @param authScopes TODO * @return the token introspection result */ - Map assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo); + Map assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo, Set authScopes); } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java index ccdae00b8..69cf1bf75 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultIntrospectionResultAssembler.java @@ -20,6 +20,7 @@ import static com.google.common.collect.Maps.newLinkedHashMap; import java.text.ParseException; import java.util.Map; +import java.util.Set; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; @@ -32,6 +33,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; import com.google.common.base.Joiner; +import com.google.common.collect.Sets; /** * Default implementation of the {@link IntrospectionResultAssembler} interface. @@ -42,14 +44,16 @@ public class DefaultIntrospectionResultAssembler implements IntrospectionResultA private static Logger log = LoggerFactory.getLogger(DefaultIntrospectionResultAssembler.class); @Override - public Map assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo) { + public Map assembleFrom(OAuth2AccessTokenEntity accessToken, UserInfo userInfo, Set authScopes) { Map result = newLinkedHashMap(); OAuth2Authentication authentication = accessToken.getAuthenticationHolder().getAuthentication(); result.put(ACTIVE, true); - result.put(SCOPE, Joiner.on(SCOPE_SEPARATOR).join(accessToken.getScope())); + Set scopes = Sets.intersection(authScopes, accessToken.getScope()); + + result.put(SCOPE, Joiner.on(SCOPE_SEPARATOR).join(scopes)); if (accessToken.getExpiration() != null) { try { @@ -78,14 +82,16 @@ public class DefaultIntrospectionResultAssembler implements IntrospectionResultA } @Override - public Map assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo) { + public Map assembleFrom(OAuth2RefreshTokenEntity refreshToken, UserInfo userInfo, Set authScopes) { Map result = newLinkedHashMap(); OAuth2Authentication authentication = refreshToken.getAuthenticationHolder().getAuthentication(); result.put(ACTIVE, true); - result.put(SCOPE, Joiner.on(SCOPE_SEPARATOR).join(authentication.getOAuth2Request().getScope())); + Set scopes = Sets.intersection(authScopes, authentication.getOAuth2Request().getScope()); + + result.put(SCOPE, Joiner.on(SCOPE_SEPARATOR).join(scopes)); if (refreshToken.getExpiration() != null) { try { diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java index 3d6e131b6..12bc6b3fd 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/IntrospectionEndpoint.java @@ -16,6 +16,8 @@ *******************************************************************************/ package org.mitre.oauth2.web; +import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -30,6 +32,8 @@ import org.mitre.openid.connect.model.UserInfo; import org.mitre.openid.connect.service.UserInfoService; import org.mitre.openid.connect.view.HttpCodeView; import org.mitre.openid.connect.view.JsonEntityView; +import org.mitre.uma.model.ResourceSet; +import org.mitre.uma.service.ResourceSetService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -61,6 +65,9 @@ public class IntrospectionEndpoint { @Autowired private UserInfoService userInfoService; + + @Autowired + private ResourceSetService resourceSetService; private static Logger logger = LoggerFactory.getLogger(IntrospectionEndpoint.class); @@ -85,6 +92,7 @@ public class IntrospectionEndpoint { } ClientDetailsEntity authClient = null; + Set authScopes = new HashSet<>(); if (auth instanceof OAuth2Authentication) { // the client authenticated with OAuth, do our UMA checks @@ -96,12 +104,28 @@ public class IntrospectionEndpoint { String authClientId = o2a.getOAuth2Request().getClientId(); authClient = clientService.loadClientByClientId(authClientId); + // the owner is the user who authorized the token in the first place + String ownerId = o2a.getUserAuthentication().getName(); + + authScopes.addAll(authClient.getScope()); + + // UMA style clients also get a subset of scopes of all the resource sets they've registered + Collection resourceSets = resourceSetService.getAllForOwnerAndClient(ownerId, authClientId); + + // collect all the scopes + for (ResourceSet rs : resourceSets) { + authScopes.addAll(rs.getScopes()); + } + } else { // the client authenticated directly, make sure it's got the right access String authClientId = auth.getName(); // direct authentication puts the client_id into the authentication's name field authClient = clientService.loadClientByClientId(authClientId); + // directly authenticated clients get a subset of any scopes that they've registered for + authScopes.addAll(authClient.getScope()); + if (!AuthenticationUtilities.hasRole(auth, "ROLE_CLIENT") || !authClient.isAllowIntrospection()) { @@ -115,19 +139,11 @@ public class IntrospectionEndpoint { } - if (authClient == null) { - // shouldn't ever get here, if the client's been authenticated by now it should exist - logger.error("Introspection client wasn't found"); - model.addAttribute("code", HttpStatus.FORBIDDEN); - return HttpCodeView.VIEWNAME; - } - // now we need to look up the token in our token stores OAuth2AccessTokenEntity accessToken = null; OAuth2RefreshTokenEntity refreshToken = null; ClientDetailsEntity tokenClient; - Set scopes; UserInfo user; try { @@ -136,7 +152,6 @@ public class IntrospectionEndpoint { accessToken = tokenServices.readAccessToken(tokenValue); tokenClient = accessToken.getClient(); - scopes = accessToken.getScope(); // get the user information of the user that authorized this token in the first place String userName = accessToken.getAuthenticationHolder().getAuthentication().getName(); @@ -150,7 +165,6 @@ public class IntrospectionEndpoint { refreshToken = tokenServices.getRefreshToken(tokenValue); tokenClient = refreshToken.getClient(); - scopes = refreshToken.getAuthenticationHolder().getAuthentication().getOAuth2Request().getScope(); // get the user information of the user that authorized this token in the first place String userName = refreshToken.getAuthenticationHolder().getAuthentication().getName(); @@ -167,10 +181,10 @@ public class IntrospectionEndpoint { // if it's a valid token, we'll print out information on it if (accessToken != null) { - Map entity = introspectionResultAssembler.assembleFrom(accessToken, user); + Map entity = introspectionResultAssembler.assembleFrom(accessToken, user, authScopes); model.addAttribute("entity", entity); } else if (refreshToken != null) { - Map entity = introspectionResultAssembler.assembleFrom(refreshToken, user); + Map entity = introspectionResultAssembler.assembleFrom(refreshToken, user, authScopes); model.addAttribute("entity", entity); } else { // no tokens were found (we shouldn't get here) diff --git a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java index 42dc44b18..5d266cdea 100644 --- a/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java +++ b/openid-connect-server/src/test/java/org/mitre/oauth2/service/impl/TestDefaultIntrospectionResultAssembler.java @@ -56,9 +56,11 @@ public class TestDefaultIntrospectionResultAssembler { authentication("name", request("clientId"))); UserInfo userInfo = userInfo("sub"); + + Set authScopes = scopes("foo", "bar", "baz"); // when - Map result = assembler.assembleFrom(accessToken, userInfo); + Map result = assembler.assembleFrom(accessToken, userInfo, authScopes); // then @@ -82,8 +84,10 @@ public class TestDefaultIntrospectionResultAssembler { OAuth2AccessTokenEntity accessToken = accessToken(new Date(123 * 1000L), scopes("foo", "bar"), "Bearer", authentication("name", request("clientId"))); + Set authScopes = scopes("foo", "bar", "baz"); + // when - Map result = assembler.assembleFrom(accessToken, null); + Map result = assembler.assembleFrom(accessToken, null, authScopes); // then @@ -109,8 +113,10 @@ public class TestDefaultIntrospectionResultAssembler { UserInfo userInfo = userInfo("sub"); + Set authScopes = scopes("foo", "bar", "baz"); + // when - Map result = assembler.assembleFrom(accessToken, userInfo); + Map result = assembler.assembleFrom(accessToken, userInfo, authScopes); // then @@ -134,8 +140,10 @@ public class TestDefaultIntrospectionResultAssembler { UserInfo userInfo = userInfo("sub"); + Set authScopes = scopes("foo", "bar", "baz"); + // when - Map result = assembler.assembleFrom(refreshToken, userInfo); + Map result = assembler.assembleFrom(refreshToken, userInfo, authScopes); // then @@ -158,8 +166,10 @@ public class TestDefaultIntrospectionResultAssembler { OAuth2RefreshTokenEntity refreshToken = refreshToken(new Date(123 * 1000L), authentication("name", request("clientId", scopes("foo", "bar")))); + Set authScopes = scopes("foo", "bar", "baz"); + // when - Map result = assembler.assembleFrom(refreshToken, null); + Map result = assembler.assembleFrom(refreshToken, null, authScopes); // then @@ -184,8 +194,10 @@ public class TestDefaultIntrospectionResultAssembler { UserInfo userInfo = userInfo("sub"); + Set authScopes = scopes("foo", "bar", "baz"); + // when - Map result = assembler.assembleFrom(refreshToken, userInfo); + Map result = assembler.assembleFrom(refreshToken, userInfo, authScopes); // then