Repository: cxf Updated Branches: refs/heads/master a802b442c -> 982bdbc9d
[CXF-6280] Prototyping an Implcit confidenatial grant service which returns a token directly to a JS client which is used by a huna user to copy tokens to confidential clients Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/982bdbc9 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/982bdbc9 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/982bdbc9 Branch: refs/heads/master Commit: 982bdbc9dc2127906d0a1ca06ae181c87c38bbfa Parents: a802b44 Author: Sergey Beryozkin <sberyoz...@talend.com> Authored: Wed Apr 22 17:15:39 2015 +0100 Committer: Sergey Beryozkin <sberyoz...@talend.com> Committed: Wed Apr 22 17:15:39 2015 +0100 ---------------------------------------------------------------------- .../oauth2/filters/OAuthRequestFilter.java | 16 +- .../services/AbstractImplicitGrantService.java | 163 +++++++++++++++++++ .../ImplicitConfidentialGrantService.java | 51 ++++++ .../oauth2/services/ImplicitGrantService.java | 130 +-------------- .../services/RedirectionBasedGrantService.java | 4 +- .../security/oauth2/utils/OAuthConstants.java | 4 + 6 files changed, 236 insertions(+), 132 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java index fe638be..22af72c 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java @@ -28,7 +28,6 @@ import javax.annotation.Priority; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.HttpMethod; import javax.ws.rs.Priorities; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.PreMatching; @@ -40,6 +39,7 @@ import javax.ws.rs.ext.Provider; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.security.SimplePrincipal; import org.apache.cxf.jaxrs.provider.FormEncodingProvider; +import org.apache.cxf.jaxrs.utils.ExceptionUtils; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; @@ -71,6 +71,7 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator private boolean checkFormData; private List<String> requiredScopes = Collections.emptyList(); private boolean allPermissionsMatch; + private boolean blockPublicClients; public void filter(ContainerRequestContext context) { validateRequest(JAXRSUtils.getCurrentMessage()); @@ -111,7 +112,7 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator || !requiredScopes.isEmpty() && requiredScopes.size() != matchingPermissions.size()) { String message = "Client has no valid permissions"; LOG.warning(message); - throw new WebApplicationException(403); + throw ExceptionUtils.toForbiddenException(null, null); } if (accessTokenV.getClientIpAddress() != null) { @@ -119,9 +120,14 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator if (remoteAddress == null || accessTokenV.getClientIpAddress().matches(remoteAddress)) { String message = "Client IP Address is invalid"; LOG.warning(message); - throw new WebApplicationException(403); + throw ExceptionUtils.toForbiddenException(null, null); } } + if (blockPublicClients && !accessTokenV.isClientConfidential()) { + String message = "Only Confidential Clients are supported"; + LOG.warning(message); + throw ExceptionUtils.toForbiddenException(null, null); + } // Create the security context and make it available on the message SecurityContext sc = createSecurityContext(req, accessTokenV); @@ -273,5 +279,9 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator public void setAllPermissionsMatch(boolean allPermissionsMatch) { this.allPermissionsMatch = allPermissionsMatch; } + + public void setBlockPublicClients(boolean blockPublicClients) { + this.blockPublicClients = blockPublicClients; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java new file mode 100644 index 0000000..63fcfa2 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java @@ -0,0 +1,163 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.oauth2.services; + +import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.utils.HttpUtils; +import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration; +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; +import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState; +import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.provider.AccessTokenResponseFilter; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; + + +public abstract class AbstractImplicitGrantService extends RedirectionBasedGrantService { + // For a client to validate that this client is a targeted recipient. + private boolean reportClientId; + private List<AccessTokenResponseFilter> responseHandlers = new LinkedList<AccessTokenResponseFilter>(); + + protected AbstractImplicitGrantService(String supportedResponseType, + String supportedGrantType) { + super(supportedResponseType, supportedGrantType); + } + + protected Response createGrant(OAuthRedirectionState state, + Client client, + List<String> requestedScope, + List<String> approvedScope, + UserSubject userSubject, + ServerAccessToken preAuthorizedToken) { + ServerAccessToken token = null; + if (preAuthorizedToken == null) { + AccessTokenRegistration reg = new AccessTokenRegistration(); + reg.setClient(client); + reg.setGrantType(super.getSupportedGrantType()); + reg.setSubject(userSubject); + reg.setRequestedScope(requestedScope); + if (approvedScope != null && approvedScope.isEmpty()) { + // no down-scoping done by a user, all of the requested scopes have been authorized + reg.setApprovedScope(requestedScope); + } else { + reg.setApprovedScope(approvedScope); + } + reg.setAudience(state.getAudience()); + token = getDataProvider().createAccessToken(reg); + } else { + token = preAuthorizedToken; + } + + ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters()); + processClientAccessToken(clientToken, token); + + // return the token by appending it as a fragment parameter to the redirect URI + + StringBuilder sb = getUriWithFragment(state.getRedirectUri()); + + sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey()); + if (state.getState() != null) { + sb.append("&"); + sb.append(OAuthConstants.STATE).append("=").append(state.getState()); + } + sb.append("&") + .append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType()); + + if (isWriteOptionalParameters()) { + sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN) + .append("=").append(clientToken.getExpiresIn()); + if (!StringUtils.isEmpty(clientToken.getApprovedScope())) { + sb.append("&").append(OAuthConstants.SCOPE).append("=") + .append(HttpUtils.queryEncode(clientToken.getApprovedScope())); + } + for (Map.Entry<String, String> entry : clientToken.getParameters().entrySet()) { + sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue())); + } + } + if (token.getRefreshToken() != null) { + processRefreshToken(sb, token.getRefreshToken()); + } + if (reportClientId) { + sb.append("&").append(OAuthConstants.CLIENT_ID).append("=").append(client.getClientId()); + } + + return Response.seeOther(URI.create(sb.toString())).build(); + } + protected void processRefreshToken(StringBuilder sb, String refreshToken) { + LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported"); + } + + protected void processClientAccessToken(ClientAccessToken clientToken, ServerAccessToken serverToken) { + for (AccessTokenResponseFilter filter : responseHandlers) { + filter.process(clientToken, serverToken); + } + } + protected Response createErrorResponse(String state, + String redirectUri, + String error) { + StringBuilder sb = getUriWithFragment(redirectUri); + sb.append(OAuthConstants.ERROR_KEY).append("=").append(error); + if (state != null) { + sb.append("&"); + sb.append(OAuthConstants.STATE).append("=").append(state); + } + + return Response.seeOther(URI.create(sb.toString())).build(); + } + + private StringBuilder getUriWithFragment(String redirectUri) { + StringBuilder sb = new StringBuilder(); + sb.append(redirectUri); + sb.append("#"); + return sb; + } + + public void setReportClientId(boolean reportClientId) { + this.reportClientId = reportClientId; + } + + public void setResponseFilters(List<AccessTokenResponseFilter> handlers) { + this.responseHandlers = handlers; + } + + public void setResponseFilter(AccessTokenResponseFilter responseHandler) { + responseHandlers.add(responseHandler); + } + + @Override + protected boolean canRedirectUriBeEmpty(Client c) { + return false; + } + @Override + protected boolean canSupportPublicClient(Client c) { + return true; + } +} + + http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java new file mode 100644 index 0000000..1847fc7 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.oauth2.services; + +import javax.ws.rs.Path; + +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; + + +/** + * Redirection-based Implicit Grant Service which returns tokens for the confidential clients + * directly to a human user. + */ +@Path("/implicit-confidential") +public class ImplicitConfidentialGrantService extends AbstractImplicitGrantService { + + public ImplicitConfidentialGrantService() { + super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_CONFIDENTIAL_GRANT); + } + + @Override + protected void processRefreshToken(StringBuilder sb, String refreshToken) { + sb.append("&").append(OAuthConstants.REFRESH_TOKEN).append("=").append(refreshToken); + } + @Override + protected boolean canSupportPublicClient(Client c) { + return false; + } + + +} + + http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java index 10542b8..a73e118 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java @@ -19,25 +19,9 @@ package org.apache.cxf.rs.security.oauth2.services; -import java.net.URI; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import javax.ws.rs.Path; -import javax.ws.rs.core.Response; -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.jaxrs.utils.HttpUtils; -import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration; -import org.apache.cxf.rs.security.oauth2.common.Client; -import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; -import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState; -import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; -import org.apache.cxf.rs.security.oauth2.common.UserSubject; -import org.apache.cxf.rs.security.oauth2.provider.AccessTokenResponseFilter; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; -import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; /** @@ -50,121 +34,11 @@ import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; * authorization code or implicit grant. */ @Path("/authorize-implicit") -public class ImplicitGrantService extends RedirectionBasedGrantService { - // For a client to validate that this client is a targeted recipient. - private boolean reportClientId; - private List<AccessTokenResponseFilter> responseHandlers = new LinkedList<AccessTokenResponseFilter>(); - +public class ImplicitGrantService extends AbstractImplicitGrantService { + public ImplicitGrantService() { super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_GRANT); } - - protected Response createGrant(OAuthRedirectionState state, - Client client, - List<String> requestedScope, - List<String> approvedScope, - UserSubject userSubject, - ServerAccessToken preAuthorizedToken) { - ServerAccessToken token = null; - if (preAuthorizedToken == null) { - AccessTokenRegistration reg = new AccessTokenRegistration(); - reg.setClient(client); - reg.setGrantType(OAuthConstants.IMPLICIT_GRANT); - reg.setSubject(userSubject); - reg.setRequestedScope(requestedScope); - if (approvedScope != null && approvedScope.isEmpty()) { - // no down-scoping done by a user, all of the requested scopes have been authorized - reg.setApprovedScope(requestedScope); - } else { - reg.setApprovedScope(approvedScope); - } - reg.setAudience(state.getAudience()); - token = getDataProvider().createAccessToken(reg); - } else { - token = preAuthorizedToken; - } - if (token.getRefreshToken() != null) { - LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported"); - } - ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters()); - processClientAccessToken(clientToken, token); - - // return the token by appending it as a fragment parameter to the redirect URI - - StringBuilder sb = getUriWithFragment(state.getRedirectUri()); - - sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey()); - if (state.getState() != null) { - sb.append("&"); - sb.append(OAuthConstants.STATE).append("=").append(state.getState()); - } - sb.append("&") - .append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType()); - - if (isWriteOptionalParameters()) { - sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN) - .append("=").append(clientToken.getExpiresIn()); - if (!StringUtils.isEmpty(clientToken.getApprovedScope())) { - sb.append("&").append(OAuthConstants.SCOPE).append("=") - .append(HttpUtils.queryEncode(clientToken.getApprovedScope())); - } - for (Map.Entry<String, String> entry : clientToken.getParameters().entrySet()) { - sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue())); - } - } - if (reportClientId) { - sb.append("&").append(OAuthConstants.CLIENT_ID).append("=").append(client.getClientId()); - } - - return Response.seeOther(URI.create(sb.toString())).build(); - } - protected void processClientAccessToken(ClientAccessToken clientToken, ServerAccessToken serverToken) { - for (AccessTokenResponseFilter filter : responseHandlers) { - filter.process(clientToken, serverToken); - } - } - protected Response createErrorResponse(String state, - String redirectUri, - String error) { - StringBuilder sb = getUriWithFragment(redirectUri); - sb.append(OAuthConstants.ERROR_KEY).append("=").append(error); - if (state != null) { - sb.append("&"); - sb.append(OAuthConstants.STATE).append("=").append(state); - } - - return Response.seeOther(URI.create(sb.toString())).build(); - } - - private StringBuilder getUriWithFragment(String redirectUri) { - StringBuilder sb = new StringBuilder(); - sb.append(redirectUri); - sb.append("#"); - return sb; - } - - public void setReportClientId(boolean reportClientId) { - this.reportClientId = reportClientId; - } - - public void setResponseFilters(List<AccessTokenResponseFilter> handlers) { - this.responseHandlers = handlers; - } - - public void setResponseFilter(AccessTokenResponseFilter responseHandler) { - responseHandlers.add(responseHandler); - } - - @Override - protected boolean canSupportPublicClient(Client c) { - return true; - } - - @Override - protected boolean canRedirectUriBeEmpty(Client c) { - return false; - } - } http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 306df30..be09cc0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -431,7 +431,9 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService protected Client getClient(MultivaluedMap<String, String> params) { return this.getClient(params.getFirst(OAuthConstants.CLIENT_ID)); } - + protected String getSupportedGrantType() { + return this.supportedGrantType; + } public void setResourceOwnerNameProvider(ResourceOwnerNameProvider resourceOwnerNameProvider) { this.resourceOwnerNameProvider = resourceOwnerNameProvider; } http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index 945c8a9..9f4a801 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -48,6 +48,10 @@ public final class OAuthConstants { public static final String RESOURCE_OWNER_GRANT = "password"; public static final String REFRESH_TOKEN_GRANT = "refresh_token"; + // CXF-specific grant + // The token is returned directly to a human user who copies it into a confidential client + public static final String IMPLICIT_CONFIDENTIAL_GRANT = "urn:ietf:params:oauth:grant-type:implicit-confidential"; + // Well-known token types public static final String BEARER_TOKEN_TYPE = "bearer"; public static final String HAWK_TOKEN_TYPE = "hawk";