You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2013/12/25 14:44:10 UTC

svn commit: r1553380 - in /chemistry/opencmis/trunk: chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/ chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemi...

Author: fmui
Date: Wed Dec 25 13:44:09 2013
New Revision: 1553380

URL: http://svn.apache.org/r1553380
Log:
added simple OAuth authentication provider

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/OAuthAuthenticationProvider.java
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/AbstractAuthenticationProvider.java
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionFactoryImpl.java
    chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java?rev=1553380&r1=1553379&r2=1553380&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java Wed Dec 25 13:44:09 2013
@@ -247,6 +247,20 @@ public class SessionParameterMap extends
     }
 
     /**
+     * Sets bearer token.
+     * 
+     * @param token
+     *            the bearer token
+     */
+    public void setBearerToken(String token) {
+        if (token == null) {
+            remove(SessionParameter.BREARER_ACCESS_TOKEN);
+        } else {
+            put(SessionParameter.BREARER_ACCESS_TOKEN, token);
+        }
+    }
+
+    /**
      * Turns all authentication off if the standard authentication provider is
      * used.
      */
@@ -325,6 +339,26 @@ public class SessionParameterMap extends
     }
 
     /**
+     * Turns OAuth 2.0 bearer token authentication on and basic authentication
+     * and UsernameToken authentication off.
+     * 
+     * @param token
+     *            the bearer token
+     */
+    public void setOAuthBearerTokenAuthentication(String token) {
+        if (token == null) {
+            throw new IllegalArgumentException("Token must be set!");
+        }
+
+        setBearerToken(token);
+
+        put(SessionParameter.AUTH_HTTP_BASIC, false);
+        put(SessionParameter.AUTH_SOAP_USERNAMETOKEN, false);
+        put(SessionParameter.AUTH_OAUTH_BEARER, true);
+        put(SessionParameter.BREARER_ACCESS_TOKEN, token);
+    }
+
+    /**
      * Sets the locale of the session.
      * 
      * @param locale

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/AbstractAuthenticationProvider.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/AbstractAuthenticationProvider.java?rev=1553380&r1=1553379&r2=1553380&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/AbstractAuthenticationProvider.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/AbstractAuthenticationProvider.java Wed Dec 25 13:44:09 2013
@@ -108,7 +108,7 @@ public abstract class AbstractAuthentica
      * @return the bearer token or {@code null} if the token is not set
      */
     protected String getBearerToken() {
-        Object tokenObject = getSession().get(SessionParameter.BREARER_TOKEN);
+        Object tokenObject = getSession().get(SessionParameter.BREARER_ACCESS_TOKEN);
         if (tokenObject instanceof String) {
             return (String) tokenObject;
         }

Added: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/OAuthAuthenticationProvider.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/OAuthAuthenticationProvider.java?rev=1553380&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/OAuthAuthenticationProvider.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/OAuthAuthenticationProvider.java Wed Dec 25 13:44:09 2013
@@ -0,0 +1,274 @@
+/*
+ * 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.chemistry.opencmis.client.bindings.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.chemistry.opencmis.client.bindings.impl.ClientVersion;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
+import org.apache.chemistry.opencmis.commons.impl.IOUtils;
+import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
+import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OAuth Authentication Provider.
+ */
+public class OAuthAuthenticationProvider extends StandardAuthenticationProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OAuthAuthenticationProvider.class);
+
+    private static final long serialVersionUID = 1L;
+
+    private String accessToken;
+    private String refreshToken;
+    private long expiresTimestamp;
+
+    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+    @Override
+    public Map<String, List<String>> getHTTPHeaders(String url) {
+        Map<String, List<String>> headers = super.getHTTPHeaders(url);
+        if (headers == null) {
+            headers = new HashMap<String, List<String>>();
+        }
+
+        headers.put("Authorization", Collections.singletonList("Bearer " + getToken()));
+
+        return headers;
+    }
+
+    /**
+     * Gets the access token. If no access token is present or the access token
+     * expired, a new token is requested.
+     * 
+     * @return the access token
+     */
+    protected String getToken() {
+        lock.writeLock().lock();
+        try {
+            if (accessToken == null) {
+                requestToken();
+            } else if (System.currentTimeMillis() >= expiresTimestamp) {
+                refreshToken();
+            }
+            return accessToken;
+        } catch (CmisConnectionException ce) {
+            throw ce;
+        } catch (Exception e) {
+            throw new CmisConnectionException("Cannot get OAuth access token: " + e.getMessage(), e);
+        } finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    private void requestToken() throws IOException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Request new OAuth access token.");
+        }
+
+        makeRequest(false);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Access token: {} / Refresh token: {}", accessToken, refreshToken);
+        }
+    }
+
+    private void refreshToken() throws IOException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Refresh new OAuth access token.");
+        }
+
+        makeRequest(true);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Access token: {} / Refresh token: {}", accessToken, refreshToken);
+        }
+    }
+
+    private void makeRequest(boolean isRefresh) throws IOException {
+        Object tokenEndpoint = getSession().get(SessionParameter.OAUTH_TOKEN_ENDPOINT);
+        if (!(tokenEndpoint instanceof String)) {
+            throw new CmisConnectionException("Token endpoint not set!");
+        }
+
+        if (isRefresh && refreshToken == null) {
+            throw new CmisConnectionException("No refresh token!");
+        }
+
+        // request token
+        HttpURLConnection conn = (HttpURLConnection) (new URL(tokenEndpoint.toString())).openConnection();
+        conn.setRequestMethod("POST");
+        conn.setDoInput(true);
+        conn.setDoOutput(true);
+        conn.setAllowUserInteraction(false);
+        conn.setUseCaches(false);
+        conn.setRequestProperty("User-Agent", ClientVersion.OPENCMIS_CLIENT);
+        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+
+        // compile request
+        Writer writer = new OutputStreamWriter(conn.getOutputStream(), IOUtils.UTF8);
+
+        if (isRefresh) {
+            writer.write("grant_type=refresh_token");
+
+            writer.write("&refresh_token=");
+            writer.write(IOUtils.encodeURL(refreshToken));
+        } else {
+            writer.write("grant_type=authorization_code");
+
+            Object code = getSession().get(SessionParameter.OAUTH_CODE);
+            if (code != null) {
+                writer.write("&code=");
+                writer.write(IOUtils.encodeURL(code.toString()));
+            }
+
+            Object redirectUri = getSession().get(SessionParameter.OAUTH_REDIRECT_URI);
+            if (redirectUri != null) {
+                writer.write("&redirect_uri=");
+                writer.write(IOUtils.encodeURL(redirectUri.toString()));
+            }
+        }
+
+        Object clientId = getSession().get(SessionParameter.OAUTH_CLIENT_ID);
+        if (clientId != null) {
+            writer.write("&client_id=");
+            writer.write(IOUtils.encodeURL(clientId.toString()));
+        }
+
+        Object clientSecret = getSession().get(SessionParameter.OAUTH_CLIENT_SECRET);
+        if (clientSecret != null) {
+            writer.write("&client_secret=");
+            writer.write(IOUtils.encodeURL(clientSecret.toString()));
+        }
+
+        writer.flush();
+
+        // connect
+        conn.connect();
+
+        // check success
+        if (conn.getResponseCode() != 200) {
+            JSONObject jsonResponse = parseResponse(conn);
+            Object error = jsonResponse.get("error");
+            Object description = jsonResponse.get("error_description");
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("OAuth token request failed: {}", jsonResponse.toJSONString());
+            }
+
+            throw new CmisConnectionException("OAuth token request failed" + (error == null ? "" : ": " + error)
+                    + (description == null ? "" : ": " + description));
+        }
+
+        // parse response
+        JSONObject jsonResponse = parseResponse(conn);
+
+        Object tokenType = jsonResponse.get("token_type");
+        if (!(tokenType instanceof String) || !"bearer".equalsIgnoreCase((String) tokenType)) {
+            throw new CmisConnectionException("Unsupported OAuth token type: " + tokenType);
+        }
+
+        Object jsonAccessToken = jsonResponse.get("access_token");
+        if (!(jsonAccessToken instanceof String)) {
+            throw new CmisConnectionException("Invalid OAuth access token!");
+        }
+
+        Object jsonRefreshToken = jsonResponse.get("refresh_token");
+        if (jsonRefreshToken != null && !(jsonRefreshToken instanceof String)) {
+            throw new CmisConnectionException("Invalid OAuth refresh token!");
+        }
+
+        Object jsonExpiresIn = jsonResponse.get("expires_in");
+        if (jsonExpiresIn != null && !(jsonExpiresIn instanceof Number)) {
+            throw new CmisConnectionException("Invalid OAuth expires in value!");
+        }
+
+        accessToken = jsonAccessToken.toString();
+        refreshToken = (jsonRefreshToken == null ? null : jsonRefreshToken.toString());
+        expiresTimestamp = (jsonExpiresIn == null ? 3600 : ((Number) jsonExpiresIn).longValue()) * 1000
+                + System.currentTimeMillis();
+    }
+
+    private JSONObject parseResponse(HttpURLConnection conn) {
+        Reader reader = null;
+        try {
+            InputStream stream = null;
+
+            int respCode = conn.getResponseCode();
+            if (respCode >= 200 && respCode < 300) {
+                stream = conn.getInputStream();
+            } else {
+                stream = conn.getErrorStream();
+            }
+            if (stream == null) {
+                throw new CmisConnectionException("Invalid OAuth token response!");
+            }
+
+            reader = new InputStreamReader(stream, extractCharset(conn));
+            JSONParser parser = new JSONParser();
+            Object response = parser.parse(reader);
+
+            if (!(response instanceof JSONObject)) {
+                throw new CmisConnectionException("Invalid OAuth token response!");
+            }
+
+            return (JSONObject) response;
+        } catch (CmisConnectionException ce) {
+            throw ce;
+        } catch (Exception pe) {
+            throw new CmisConnectionException("Parsing the OAuth token response failed: " + pe.getMessage(), pe);
+        } finally {
+            IOUtils.consumeAndClose(reader);
+        }
+    }
+
+    private String extractCharset(HttpURLConnection conn) {
+        String charset = IOUtils.UTF8;
+
+        String contentType = conn.getContentType();
+        if (contentType != null) {
+            String[] parts = contentType.split(";");
+            for (int i = 1; i < parts.length; i++) {
+                String part = parts[i].trim().toLowerCase();
+                if (part.startsWith("charset")) {
+                    int x = part.indexOf('=');
+                    charset = part.substring(x + 1).trim();
+                    break;
+                }
+            }
+        }
+
+        return charset;
+    }
+}

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionFactoryImpl.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionFactoryImpl.java?rev=1553380&r1=1553379&r2=1553380&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionFactoryImpl.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-impl/src/main/java/org/apache/chemistry/opencmis/client/runtime/SessionFactoryImpl.java Wed Dec 25 13:44:09 2013
@@ -113,7 +113,8 @@ public class SessionFactoryImpl implemen
 
         List<Repository> result = new ArrayList<Repository>();
         for (RepositoryInfo data : repositoryInfos) {
-            result.add(new RepositoryImpl(data, parameters, this, objectFactory, authenticationProvider, cache));
+            result.add(new RepositoryImpl(data, parameters, this, objectFactory, binding.getAuthenticationProvider(),
+                    cache));
         }
 
         return result;

Modified: chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java?rev=1553380&r1=1553379&r2=1553380&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java Wed Dec 25 13:44:09 2013
@@ -500,7 +500,6 @@ public final class SessionParameter {
     // ---- general parameter ----
     public static final String USER = "org.apache.chemistry.opencmis.user";
     public static final String PASSWORD = "org.apache.chemistry.opencmis.password";
-    public static final String BREARER_TOKEN = "org.apache.chemistry.opencmis.oauth.brearertoken";
 
     // --- binding parameter ----
     /** Predefined binding types (see {@code BindingType}). */
@@ -553,6 +552,8 @@ public final class SessionParameter {
     /** Factory class name for the local binding. */
     public static final String LOCAL_FACTORY = "org.apache.chemistry.opencmis.binding.local.classname";
 
+    // --- authentication ---
+
     /** Class name of the authentication provider. */
     public static final String AUTHENTICATION_PROVIDER_CLASS = "org.apache.chemistry.opencmis.binding.auth.classname";
 
@@ -574,6 +575,16 @@ public final class SessionParameter {
      */
     public static final String AUTH_SOAP_USERNAMETOKEN = "org.apache.chemistry.opencmis.binding.auth.soap.usernametoken";
 
+    // --- OAuth ---
+
+    public static final String OAUTH_CLIENT_ID = "org.apache.chemistry.opencmis.oauth.clientId";
+    public static final String OAUTH_CLIENT_SECRET = "org.apache.chemistry.opencmis.oauth.clientSecret";
+    public static final String OAUTH_CODE = "org.apache.chemistry.opencmis.oauth.code";
+    public static final String OAUTH_TOKEN_ENDPOINT = "org.apache.chemistry.opencmis.oauth.tokenEndpoint";
+    public static final String OAUTH_REDIRECT_URI = "org.apache.chemistry.opencmis.oauth.redirectUri";
+
+    public static final String BREARER_ACCESS_TOKEN = "org.apache.chemistry.opencmis.oauth.accessToken";
+
     // --- connection ---
 
     public static final String HTTP_INVOKER_CLASS = "org.apache.chemistry.opencmis.binding.httpinvoker.classname";