You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2013/06/06 13:00:25 UTC

svn commit: r1490236 - in /cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2: provider/ services/ utils/

Author: sergeyb
Date: Thu Jun  6 11:00:24 2013
New Revision: 1490236

URL: http://svn.apache.org/r1490236
Log:
[CXF-5060] Adding Token Revocation service

Added:
    cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java   (with props)
    cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java   (with props)
Modified:
    cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthDataProvider.java
    cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenService.java
    cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java

Modified: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthDataProvider.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthDataProvider.java?rev=1490236&r1=1490235&r2=1490236&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthDataProvider.java (original)
+++ cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthDataProvider.java Thu Jun  6 11:00:24 2013
@@ -86,11 +86,20 @@ public interface OAuthDataProvider {
         throws OAuthServiceException;
 
     /**
-     * Removes the token
+     * Removes the access token
+     * The runtime will call this method if it finds that a token has expired
      * @param accessToken the token
      * @throws OAuthServiceException
      */
     void removeAccessToken(ServerAccessToken accessToken) throws OAuthServiceException;
+    
+    /**
+     * Revokes a refresh or access token
+     * @param token token identifier
+     * @param tokenTypeHint 
+     * @throws OAuthServiceException
+     */
+    void revokeToken(Client client, String token, String tokenTypeHint) throws OAuthServiceException;
 
     /**
      * Converts the requested scope to the list of permissions  

Added: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java?rev=1490236&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java (added)
+++ cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java Thu Jun  6 11:00:24 2013
@@ -0,0 +1,149 @@
+/**
+ * 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.security.Principal;
+
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.OAuthError;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.cxf.rs.security.oauth2.utils.AuthorizationUtils;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+public class AbstractTokenService extends AbstractOAuthService {
+    private boolean canSupportPublicClients;
+    private boolean writeCustomErrors;
+    
+    /**
+     * Make sure the client is authenticated
+     */
+    protected Client authenticateClientIfNeeded(MultivaluedMap<String, String> params) {
+        Client client = null;
+        SecurityContext sc = getMessageContext().getSecurityContext();
+        
+        if (params.containsKey(OAuthConstants.CLIENT_ID)) {
+            // both client_id and client_secret are expected in the form payload
+            client = getAndValidateClient(params.getFirst(OAuthConstants.CLIENT_ID),
+                                          params.getFirst(OAuthConstants.CLIENT_SECRET));
+        } else if (sc.getUserPrincipal() != null) {
+            // client has already authenticated
+            Principal p = sc.getUserPrincipal();
+            String scheme = sc.getAuthenticationScheme();
+            if (OAuthConstants.BASIC_SCHEME.equalsIgnoreCase(scheme)) {
+                // section 2.3.1
+                client = getClient(p.getName());
+            } else {
+                // section 2.3.2
+                // the client has authenticated itself using some other scheme
+                // in which case the mapping between the scheme and the client_id
+                // should've been done and the client_id is expected
+                // on the current message
+                Object clientIdProp = getMessageContext().get(OAuthConstants.CLIENT_ID);
+                if (clientIdProp != null) {
+                    client = getClient(clientIdProp.toString());
+                    // TODO: consider matching client.getUserSubject().getLoginName() 
+                    // against principal.getName() ?
+                }
+            }
+        } else {
+            // the client id and secret are expected to be in the Basic scheme data
+            String[] parts = 
+                AuthorizationUtils.getAuthorizationParts(getMessageContext());
+            if (OAuthConstants.BASIC_SCHEME.equalsIgnoreCase(parts[0])) {
+                String[] authInfo = AuthorizationUtils.getBasicAuthParts(parts[1]);
+                client = getAndValidateClient(authInfo[0], authInfo[1]);
+            }
+        }
+        
+        if (client == null) {
+            throw new NotAuthorizedException(Response.status(401).build());
+        }
+        return client;
+    }
+    
+    // Get the Client and check the id and secret
+    protected Client getAndValidateClient(String clientId, String clientSecret) {
+        Client client = getClient(clientId);
+        if (canSupportPublicClients 
+            && !client.isConfidential() 
+            && client.getClientSecret() == null 
+            && client.getRedirectUris().isEmpty()
+            && clientSecret == null) {
+            return client;
+        }
+        if (clientSecret == null || client.getClientSecret() == null 
+            || !client.getClientId().equals(clientId) 
+            || !client.getClientSecret().equals(clientSecret)) {
+            throw new NotAuthorizedException(Response.status(401).build());
+        }
+        return client;
+    }
+    
+    protected Response handleException(OAuthServiceException ex, String error) {
+        OAuthError customError = ex.getError();
+        if (writeCustomErrors && customError != null) {
+            return createErrorResponseFromBean(customError);
+        } else {
+            return createErrorResponseFromBean(new OAuthError(error));
+        }
+    }
+    
+    protected Response createErrorResponse(MultivaluedMap<String, String> params,
+                                           String error) {
+        return createErrorResponseFromBean(new OAuthError(error));
+    }
+    
+    protected Response createErrorResponseFromBean(OAuthError errorBean) {
+        return Response.status(400).entity(errorBean).build();
+    }
+    
+    /**
+     * Get the {@link Client} reference
+     * @param clientId the provided client id
+     * @return Client the client reference 
+     * @throws {@link javax.ws.rs.WebApplicationException} if no matching Client is found
+     */
+    protected Client getClient(String clientId) {
+        Client client = null;
+        try {
+            client = getValidClient(clientId);
+        } catch (OAuthServiceException ex) {
+            // log it
+        }
+        if (client == null) {
+            reportInvalidRequestError("Client ID is invalid");
+        }
+        return client;
+        
+    }
+    
+    public void setCanSupportPublicClients(boolean support) {
+        this.canSupportPublicClients = support;
+    }
+
+    public void setWriteCustomErrors(boolean writeCustomErrors) {
+        this.writeCustomErrors = writeCustomErrors;
+    }
+}

Propchange: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenService.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenService.java?rev=1490236&r1=1490235&r2=1490236&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenService.java (original)
+++ cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenService.java Thu Jun  6 11:00:24 2013
@@ -19,30 +19,25 @@
 
 package org.apache.cxf.rs.security.oauth2.services;
 
-import java.security.Principal;
 import java.util.Collections;
 import java.util.List;
 
 import javax.ws.rs.Consumes;
-import javax.ws.rs.NotAuthorizedException;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
 
 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.OAuthError;
 import org.apache.cxf.rs.security.oauth2.common.OAuthPermission;
 import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken;
 import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeDataProvider;
 import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrantHandler;
 import org.apache.cxf.rs.security.oauth2.provider.AccessTokenGrantHandler;
 import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
-import org.apache.cxf.rs.security.oauth2.utils.AuthorizationUtils;
 import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
 import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
 
@@ -50,14 +45,8 @@ import org.apache.cxf.rs.security.oauth2
  * OAuth2 Access Token Service implementation
  */
 @Path("/token")
-public class AccessTokenService extends AbstractOAuthService {
+public class AccessTokenService extends AbstractTokenService {
     private List<AccessTokenGrantHandler> grantHandlers = Collections.emptyList();
-    private boolean writeCustomErrors;
-    private boolean canSupportPublicClients;
-    
-    public void setWriteCustomErrors(boolean write) {
-        writeCustomErrors = write;
-    }
     
     /**
      * Sets the list of optional grant handlers
@@ -91,11 +80,7 @@ public class AccessTokenService extends 
         try {
             serverToken = handler.createAccessToken(client, params);
         } catch (OAuthServiceException ex) {
-            OAuthError customError = ex.getError();
-            if (writeCustomErrors && customError != null) {
-                return createErrorResponseFromBean(customError);
-            }
-
+            return handleException(ex, OAuthConstants.INVALID_GRANT);
         }
         if (serverToken == null) {
             return createErrorResponse(params, OAuthConstants.INVALID_GRANT);
@@ -122,71 +107,6 @@ public class AccessTokenService extends 
     }
     
     /**
-     * Make sure the client is authenticated
-     */
-    private Client authenticateClientIfNeeded(MultivaluedMap<String, String> params) {
-        Client client = null;
-        SecurityContext sc = getMessageContext().getSecurityContext();
-        
-        if (params.containsKey(OAuthConstants.CLIENT_ID)) {
-            // both client_id and client_secret are expected in the form payload
-            client = getAndValidateClient(params.getFirst(OAuthConstants.CLIENT_ID),
-                                          params.getFirst(OAuthConstants.CLIENT_SECRET));
-        } else if (sc.getUserPrincipal() != null) {
-            // client has already authenticated
-            Principal p = sc.getUserPrincipal();
-            String scheme = sc.getAuthenticationScheme();
-            if (OAuthConstants.BASIC_SCHEME.equalsIgnoreCase(scheme)) {
-                // section 2.3.1
-                client = getClient(p.getName());
-            } else {
-                // section 2.3.2
-                // the client has authenticated itself using some other scheme
-                // in which case the mapping between the scheme and the client_id
-                // should've been done and the client_id is expected
-                // on the current message
-                Object clientIdProp = getMessageContext().get(OAuthConstants.CLIENT_ID);
-                if (clientIdProp != null) {
-                    client = getClient(clientIdProp.toString());
-                    // TODO: consider matching client.getUserSubject().getLoginName() 
-                    // against principal.getName() ?
-                }
-            }
-        } else {
-            // the client id and secret are expected to be in the Basic scheme data
-            String[] parts = 
-                AuthorizationUtils.getAuthorizationParts(getMessageContext());
-            if (OAuthConstants.BASIC_SCHEME.equalsIgnoreCase(parts[0])) {
-                String[] authInfo = AuthorizationUtils.getBasicAuthParts(parts[1]);
-                client = getAndValidateClient(authInfo[0], authInfo[1]);
-            }
-        }
-        
-        if (client == null) {
-            throw new NotAuthorizedException(Response.status(401).build());
-        }
-        return client;
-    }
-    
-    // Get the Client and check the id and secret
-    private Client getAndValidateClient(String clientId, String clientSecret) {
-        Client client = getClient(clientId);
-        if (canSupportPublicClients 
-            && !client.isConfidential() 
-            && client.getClientSecret() == null 
-            && client.getRedirectUris().isEmpty()
-            && clientSecret == null) {
-            return client;
-        }
-        if (clientSecret == null || client.getClientSecret() == null 
-            || !client.getClientId().equals(clientId) 
-            || !client.getClientSecret().equals(clientSecret)) {
-            throw new NotAuthorizedException(Response.status(401).build());
-        }
-        return client;
-    }
-    
-    /**
      * Find the mathcing grant handler
      */
     protected AccessTokenGrantHandler findGrantHandler(MultivaluedMap<String, String> params) {
@@ -210,37 +130,4 @@ public class AccessTokenService extends 
         
         return null;
     }
-    
-    protected Response createErrorResponse(MultivaluedMap<String, String> params,
-                                           String error) {
-        return createErrorResponseFromBean(new OAuthError(error));
-    }
-    
-    protected Response createErrorResponseFromBean(OAuthError errorBean) {
-        return Response.status(400).entity(errorBean).build();
-    }
-    
-    /**
-     * Get the {@link Client} reference
-     * @param clientId the provided client id
-     * @return Client the client reference 
-     * @throws {@link javax.ws.rs.WebApplicationException} if no matching Client is found
-     */
-    protected Client getClient(String clientId) {
-        Client client = null;
-        try {
-            client = getValidClient(clientId);
-        } catch (OAuthServiceException ex) {
-            // log it
-        }
-        if (client == null) {
-            reportInvalidRequestError("Client ID is invalid");
-        }
-        return client;
-        
-    }
-    
-    public void setCanSupportPublicClients(boolean support) {
-        this.canSupportPublicClients = support;
-    }
-}
+}
\ No newline at end of file

Added: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java?rev=1490236&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java (added)
+++ cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java Thu Jun  6 11:00:24 2013
@@ -0,0 +1,63 @@
+/**
+ * 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.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+/**
+ * OAuth2 Token Revocation Service implementation
+ */
+@Path("/revoke")
+public class TokenRevocationService extends AbstractTokenService {
+    
+    /**
+     * Processes a token revocation request
+     * @param params the form parameters representing the access token grant 
+     * @return Access Token or the error 
+     */
+    @POST
+    @Consumes("application/x-www-form-urlencoded")
+    @Produces("application/json")
+    public Response handleTokenRevocation(MultivaluedMap<String, String> params) {
+        
+        // Make sure the client is authenticated
+        Client client = authenticateClientIfNeeded(params);
+        String token = params.getFirst(OAuthConstants.REVOKED_TOKEN_ID);
+        if (token == null) {
+            return createErrorResponse(params, OAuthConstants.UNSUPPORTED_TOKEN_TYPE);
+        }
+        String tokenTypeHint = params.getFirst(OAuthConstants.REVOKED_TOKEN_TYPE_HINT);
+        try {
+            getDataProvider().revokeToken(client, token, tokenTypeHint);
+        } catch (OAuthServiceException ex) {
+            return handleException(ex, OAuthConstants.UNSUPPORTED_TOKEN_TYPE);
+        }
+        return Response.ok().build();
+    }
+}
\ No newline at end of file

Propchange: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/TokenRevocationService.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java?rev=1490236&r1=1490235&r2=1490236&view=diff
==============================================================================
--- cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java (original)
+++ cxf/trunk/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java Thu Jun  6 11:00:24 2013
@@ -98,6 +98,11 @@ public final class OAuthConstants {
     public static final String INVALID_SCOPE = "invalid_scope";
     public static final String ACCESS_DENIED = "access_denied";
     
+    // Token Revocation
+    public static final String REVOKED_TOKEN_ID = "token";
+    public static final String REVOKED_TOKEN_TYPE_HINT = "token_type_hint";
+    public static final String UNSUPPORTED_TOKEN_TYPE = "unsupported_token_type";
+    
     // CXF-Specific parameters
     public static final String ACCESS_TOKEN_ISSUED_AT = "issued_at";
     // End Of CXF-Specific