Author: [email protected]
Date: Wed Jul 13 12:56:22 2011
New Revision: 1212

Log:
[AMDATUAUTH-65] Implemented a custom validator which uses a pluggable nonce 
storage provider. The oauth project comes with an in-memory storage, which is 
obviously still not distributed. The new pluggable approach however facilitates 
implementing a Cassandra storage for nonces, making it distributed.

Added:
   
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthNonceStorageProvider.java
   
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/PluggableOAuthValidator.java
   trunk/amdatu-auth/oauth-stores/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/pom.xml
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/osgi/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/osgi/Activator.java
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/service/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/service/InMemNonceStorageProviderImpl.java
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/
   trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/test/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/test/unit/
   
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/test/unit/NonceStoreTest.java
   trunk/amdatu-auth/oauth-stores/pom.xml
Modified:
   
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/osgi/Activator.java
   
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
   
trunk/amdatu-auth/test-integration/base/src/main/java/org/amdatu/auth/test/integration/base/AuthFixture.java
   trunk/amdatu-auth/test-integration/tests/pom.xml

Added: 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthNonceStorageProvider.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/OAuthNonceStorageProvider.java
  Wed Jul 13 12:56:22 2011
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ * 
+ * Licensed 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.amdatu.authentication.oauth.server;
+
+/**
+ * Pluggable nonce store.
+ * 
+ * @author ivol
+ */
+public interface OAuthNonceStorageProvider {
+    /**
+     * Adds a nonce to the store and returns true if the store did not yet 
contain it. If the nonce
+     * is already contained by the store, the nonce is not added/updated and 
false is returned. 
+     * Note that two nonces are considered to be the same in case ALL input 
parameters of this method
+     * are exactly the same. The sole purpose of the nonce is to prevent 
replay attacks. If the timestamp
+     * or any other request parameter in the request is different, it is not a 
replay attack since it 
+     * is not the same request.
+     * 
+     * @param timestamp
+     *        The timestamp of the request that contains the nonce
+     * @param nonce
+     *        The nonce itself
+     * @param requestParams
+     *          Any additional parameters send along with the request that 
contained the nonce and should
+     *          be associated with the nonce. Note that two nonces with 
different request parameters are
+     *          not considered to be the same. For example, adding the 
consumer key as request parameter
+     *          and associating it with a nonce prevents that if two service 
consumers both use the
+     *          auto-increment index (1,2,3,...) approach to generate the 
nonce, they would run into
+     *          conflicts as they are trying to use the same nonces. Note that 
such approach would be a
+     *          bad design decision, a nonce is supposed to be a long and 
random number or string.
+
+     * @return true if the nonce did not yet exist and was added to the store, 
false otherwise
+     */
+    boolean addNonce(long timestamp, String nonce, String[] requestParams);
+
+    /**
+     * Removes expired nonces. All nonces which have a timestamp that is older 
(smaller: <) then the provided timestamp
+     * are removed.
+     * 
+     * @param timestamp
+     */
+    void removeExpiredNonces(long timestamp);
+}

Modified: 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/osgi/Activator.java
==============================================================================
--- 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/osgi/Activator.java
     (original)
+++ 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/osgi/Activator.java
     Wed Jul 13 12:56:22 2011
@@ -24,6 +24,7 @@
 import org.amdatu.authentication.oauth.api.OAuthServiceProvider;
 import org.amdatu.authentication.oauth.server.OAuthAccessTokenServlet;
 import org.amdatu.authentication.oauth.server.OAuthAuthorizeTokenServlet;
+import org.amdatu.authentication.oauth.server.OAuthNonceStorageProvider;
 import org.amdatu.authentication.oauth.server.OAuthRequestTokenServlet;
 import org.amdatu.authentication.oauth.server.OAuthServerConfig;
 import org.amdatu.authentication.oauth.server.OAuthServiceConsumerRegistryREST;
@@ -87,6 +88,7 @@
                 
.add(createServiceDependency().setService(TokenStorageProvider.class).setRequired(true))
                 
.add(createServiceDependency().setService(OAuthServiceProvider.class).setRequired(true))
                 
.add(createServiceDependency().setService(OAuthServerConfiguration.class).setRequired(true))
+                
.add(createServiceDependency().setService(OAuthNonceStorageProvider.class).setRequired(true))
                 
.add(createServiceDependency().setService(OAuthServiceConsumerRegistry.class).setRequired(true)));
 
         // Create and register the resource provider

Modified: 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
==============================================================================
--- 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
     (original)
+++ 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/OAuthTokenProviderImpl.java
     Wed Jul 13 12:56:22 2011
@@ -26,13 +26,12 @@
 import net.oauth.OAuthException;
 import net.oauth.OAuthMessage;
 import net.oauth.OAuthProblemException;
-import net.oauth.OAuthValidator;
-import net.oauth.SimpleOAuthValidator;
 
 import org.amdatu.authentication.oauth.api.ConsumerRegistryStorageException;
 import org.amdatu.authentication.oauth.api.OAuthServiceConsumer;
 import org.amdatu.authentication.oauth.api.OAuthServiceConsumerRegistry;
 import org.amdatu.authentication.oauth.api.OAuthServiceProvider;
+import org.amdatu.authentication.oauth.server.OAuthNonceStorageProvider;
 import org.amdatu.authentication.oauth.server.OAuthTokenProvider;
 import org.amdatu.authentication.tokenprovider.Token;
 import org.amdatu.authentication.tokenprovider.TokenStorageProvider;
@@ -50,9 +49,10 @@
     private volatile OAuthServiceConsumerRegistry m_consumerRegistry;
     private volatile TokenStorageProvider m_tokenStore;
     private volatile OAuthServerConfiguration m_config;
+    private volatile OAuthNonceStorageProvider m_nonceStore;
 
     // The simple oAuth validator.
-    private OAuthValidator m_oAuthValidator;
+    private PluggableOAuthValidator m_oAuthValidator;
 
     // Other private members
     private String m_tenantId;
@@ -69,7 +69,7 @@
             // we replace it with the maximum long value (which equals 292 
million years, quite close to infinite)
             requestMaxAge = Long.MAX_VALUE;
         }
-        m_oAuthValidator = new SimpleOAuthValidator(requestMaxAge, 
Double.parseDouble(OAuth.VERSION_1_0));
+        m_oAuthValidator = new PluggableOAuthValidator(requestMaxAge, 
Double.parseDouble(OAuth.VERSION_1_0), m_nonceStore);
 
         m_logService.log(LogService.LOG_DEBUG, "OAuthTokenProvider service 
started for tenant '" + m_tenantId + "'");
     }

Added: 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/PluggableOAuthValidator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-auth/oauth-server/src/main/java/org/amdatu/authentication/oauth/server/service/PluggableOAuthValidator.java
    Wed Jul 13 12:56:22 2011
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ * 
+ * Licensed 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.amdatu.authentication.oauth.server.service;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthProblemException;
+import net.oauth.OAuthValidator;
+import net.oauth.signature.OAuthSignatureMethod;
+
+import org.amdatu.authentication.oauth.server.OAuthNonceStorageProvider;
+
+public class PluggableOAuthValidator implements OAuthValidator {
+
+    /** The default maximum age of timestamps is 5 minutes. */
+    public static final long DEFAULT_MAX_TIMESTAMP_AGE = 5 * 60 * 1000L;
+    public static final long DEFAULT_TIMESTAMP_WINDOW = 
DEFAULT_MAX_TIMESTAMP_AGE;
+
+    /**
+     * Names of parameters that may not appear twice in a valid message.
+     * This limitation is specified by OAuth Core <a
+     * href="http://oauth.net/core/1.0#anchor7";>section 5</a>.
+     */
+    public static final Set<String> SINGLE_PARAMETERS = 
constructSingleParameters();
+
+    private static Set<String> constructSingleParameters() {
+        Set<String> s = new HashSet<String>();
+        for (String p : new String[] {OAuth.OAUTH_CONSUMER_KEY, 
OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET,
+                OAuth.OAUTH_CALLBACK, OAuth.OAUTH_SIGNATURE_METHOD, 
OAuth.OAUTH_SIGNATURE, OAuth.OAUTH_TIMESTAMP,
+                OAuth.OAUTH_NONCE, OAuth.OAUTH_VERSION}) {
+            s.add(p);
+        }
+        return Collections.unmodifiableSet(s);
+    }
+
+    protected final double m_minVersion = 1.0;
+    protected final double m_maxVersion;
+    protected final long m_maxTimestampAgeMsec;
+    private OAuthNonceStorageProvider m_nonceStore;
+
+    /**
+     * Construct a validator that rejects messages more than five minutes old 
or
+     * with a OAuth version other than 1.0.
+     */
+    public PluggableOAuthValidator(OAuthNonceStorageProvider nonceStore) {
+        this(DEFAULT_TIMESTAMP_WINDOW, Double.parseDouble(OAuth.VERSION_1_0), 
nonceStore);
+    }
+
+    /**
+     * Public constructor.
+     * 
+     * @param maxTimestampAgeMsec
+     *        the range of valid timestamps, in milliseconds into the past
+     *        or future. So the total range of valid timestamps is twice
+     *        this value, rounded to the nearest second.
+     * @param maxVersion
+     *        the maximum valid oauth_version
+     */
+    public PluggableOAuthValidator(long maxTimestampAgeMsec, double 
maxVersion, OAuthNonceStorageProvider nonceStore) {
+        m_maxTimestampAgeMsec = maxTimestampAgeMsec;
+        m_maxVersion = maxVersion;
+        m_nonceStore = nonceStore;
+    }
+
+    /**
+     * Remove usedNonces with timestamps that are too old to be valid.
+     */
+    private void removeOldNonces(long currentTimeMsec) {
+        long minTimestamp = currentTimeMsec - m_maxTimestampAgeMsec;
+        m_nonceStore.removeExpiredNonces(minTimestamp);
+    }
+
+    /**
+     * {@inherit}
+     * 
+     * @throws URISyntaxException
+     */
+    public void validateMessage(OAuthMessage message, OAuthAccessor accessor)
+        throws OAuthException, IOException, URISyntaxException {
+        checkSingleParameters(message);
+        validateVersion(message);
+        validateTimestampAndNonce(message);
+        validateSignature(message, accessor);
+    }
+
+    /** Throw an exception if any SINGLE_PARAMETERS occur repeatedly. */
+    protected void checkSingleParameters(OAuthMessage message) throws 
IOException, OAuthException {
+        // Check for repeated oauth_ parameters:
+        boolean repeated = false;
+        Map<String, Collection<String>> nameToValues = new HashMap<String, 
Collection<String>>();
+        for (Map.Entry<String, String> parameter : message.getParameters()) {
+            String name = parameter.getKey();
+            if (SINGLE_PARAMETERS.contains(name)) {
+                Collection<String> values = nameToValues.get(name);
+                if (values == null) {
+                    values = new ArrayList<String>();
+                    nameToValues.put(name, values);
+                }
+                else {
+                    repeated = true;
+                }
+                values.add(parameter.getValue());
+            }
+        }
+        if (repeated) {
+            Collection<OAuth.Parameter> rejected = new 
ArrayList<OAuth.Parameter>();
+            for (Map.Entry<String, Collection<String>> p : 
nameToValues.entrySet()) {
+                String name = p.getKey();
+                Collection<String> values = p.getValue();
+                if (values.size() > 1) {
+                    for (String value : values) {
+                        rejected.add(new OAuth.Parameter(name, value));
+                    }
+                }
+            }
+            OAuthProblemException problem = new 
OAuthProblemException(OAuth.Problems.PARAMETER_REJECTED);
+            problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_REJECTED, 
OAuth.formEncode(rejected));
+            throw problem;
+        }
+    }
+
+    protected void validateVersion(OAuthMessage message)
+        throws OAuthException, IOException {
+        String versionString = message.getParameter(OAuth.OAUTH_VERSION);
+        if (versionString != null) {
+            double version = Double.parseDouble(versionString);
+            if (version < m_minVersion || m_maxVersion < version) {
+                OAuthProblemException problem = new 
OAuthProblemException(OAuth.Problems.VERSION_REJECTED);
+                problem.setParameter(OAuth.Problems.OAUTH_ACCEPTABLE_VERSIONS, 
m_minVersion + "-" + m_maxVersion);
+                throw problem;
+            }
+        }
+    }
+
+    /**
+     * Throw an exception if the timestamp is out of range or the nonce has 
been
+     * validated previously.
+     */
+    protected void validateTimestampAndNonce(OAuthMessage message)
+        throws IOException, OAuthProblemException {
+        message.requireParameters(OAuth.OAUTH_TIMESTAMP, OAuth.OAUTH_NONCE);
+        long timestamp = 
Long.parseLong(message.getParameter(OAuth.OAUTH_TIMESTAMP));
+        long now = currentTimeMsec();
+        validateTimestamp(message, timestamp, now);
+        validateNonce(message, timestamp, now);
+    }
+
+    /** Throw an exception if the timestamp [sec] is out of range. */
+    protected void validateTimestamp(OAuthMessage message, long timestamp, 
long currentTimeMsec) throws IOException,
+            OAuthProblemException {
+        long min = (currentTimeMsec - m_maxTimestampAgeMsec + 500) / 1000L;
+        long max = (currentTimeMsec + m_maxTimestampAgeMsec + 500) / 1000L;
+        if (timestamp < min || max < timestamp) {
+            OAuthProblemException problem = new 
OAuthProblemException(OAuth.Problems.TIMESTAMP_REFUSED);
+            problem.setParameter(OAuth.Problems.OAUTH_ACCEPTABLE_TIMESTAMPS, 
min + "-" + max);
+            throw problem;
+        }
+    }
+
+    /**
+     * Throw an exception if the nonce has been validated previously.
+     * 
+     * @return the earliest point in time at which a call to releaseGarbage
+     *         will actually release some garbage, or null to indicate there's
+     *         nothing currently stored that will become garbage in future.
+     */
+    protected void validateNonce(OAuthMessage message, long timestamp, long 
currentTimeMsec) throws IOException,
+            OAuthProblemException {
+        String nonce = message.getParameter(OAuth.OAUTH_NONCE);
+        // Associate the consumer key and token with the nonce, such that 
requests from a different
+        // consumer or using a different token will never result in a nonce 
conflict (which could happen
+        // if they use the same nonce by coincidence)
+        String[] requestParams = new String[]{message.getConsumerKey(), 
message.getToken()};
+        boolean valid = m_nonceStore.addNonce(currentTimeMsec, nonce, 
requestParams);
+        if (!valid) {
+            throw new OAuthProblemException(OAuth.Problems.NONCE_USED);
+        }
+        removeOldNonces(currentTimeMsec);
+    }
+
+    protected void validateSignature(OAuthMessage message, OAuthAccessor 
accessor)
+        throws OAuthException, IOException, URISyntaxException {
+        message.requireParameters(OAuth.OAUTH_CONSUMER_KEY,
+                OAuth.OAUTH_SIGNATURE_METHOD, OAuth.OAUTH_SIGNATURE);
+        OAuthSignatureMethod.newSigner(message, accessor).validate(message);
+    }
+
+    /** Get the number of milliseconds since midnight, January 1, 1970 UTC. */
+    protected long currentTimeMsec() {
+        return System.currentTimeMillis();
+    }
+}

Added: trunk/amdatu-auth/oauth-stores/nonce-store-mem/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-auth/oauth-stores/nonce-store-mem/pom.xml      Wed Jul 13 
12:56:22 2011
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2010, 2011 The Amdatu Foundation
+
+  Licensed 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.verning permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu.auth</groupId>
+    <artifactId>org.amdatu.auth.oauth.stores</artifactId>
+    <version>0.2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>org.amdatu.auth.oauth.store.nonce.mem</artifactId>
+  <packaging>bundle</packaging>
+  <name>Amdatu Auth - OAuth filebased nonce store</name>
+  <description>This bundle implements a persistent storage for oAuth 
nonces</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.amdatu.auth</groupId>
+      <artifactId>org.amdatu.auth.oauth.server</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.libraries</groupId>
+      <artifactId>org.amdatu.libraries.fsstorage</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            
<Bundle-Activator>org.amdatu.oauth.store.nonce.mem.osgi.Activator</Bundle-Activator>
+            
<Bundle-SymbolicName>org.amdatu.oauth.store.nonce.mem</Bundle-SymbolicName>
+            <Embed-Dependency>*;scope=compile</Embed-Dependency>
+            <Import-Package>
+              org.amdatu.authentication.oauth.server,
+              *
+            </Import-Package>
+            <Export-Package></Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

Added: 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/osgi/Activator.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/osgi/Activator.java
   Wed Jul 13 12:56:22 2011
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed 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.amdatu.oauth.store.nonce.mem.osgi;
+
+import org.amdatu.authentication.oauth.server.OAuthNonceStorageProvider;
+import org.amdatu.oauth.store.nonce.mem.service.InMemNonceStorageProviderImpl;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class represents the OSGi activator for the tenant service fs storage 
provider.
+ */
+public final class Activator extends DependencyActivatorBase {
+
+    @Override
+    public void init(final BundleContext context, final DependencyManager 
manager) throws Exception {
+
+        manager.add(
+                createComponent()
+                    .setImplementation(InMemNonceStorageProviderImpl.class)
+                    .setInterface(OAuthNonceStorageProvider.class.getName(), 
null)
+                    
.add(createServiceDependency().setService(LogService.class).setRequired(true)));
+    }
+
+    @Override
+    public void destroy(final BundleContext context, final DependencyManager 
manager) throws Exception {
+    }
+}

Added: 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/service/InMemNonceStorageProviderImpl.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/main/java/org/amdatu/oauth/store/nonce/mem/service/InMemNonceStorageProviderImpl.java
    Wed Jul 13 12:56:22 2011
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2010, 2011 The Amdatu Foundation
+ *
+ * Licensed 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.amdatu.oauth.store.nonce.mem.service;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.amdatu.authentication.oauth.server.OAuthNonceStorageProvider;
+
+public class InMemNonceStorageProviderImpl implements 
OAuthNonceStorageProvider {
+    private final Set<UsedNonce> usedNonces = new TreeSet<UsedNonce>();
+
+    public boolean addNonce(long timestamp, String nonce, String[] 
requestParams) {
+        String[] nonceEtc = requestParams != null ? new 
String[requestParams.length + 1] : new String[1];
+        nonceEtc[0] = nonce;
+        if (requestParams != null) {
+            for (int i = 0; i < requestParams.length; i++) {
+                nonceEtc[i + 1] = requestParams[i];
+            }
+        }
+        UsedNonce usedNonce = new UsedNonce(timestamp, nonceEtc);
+        return usedNonces.add(usedNonce);
+    }
+
+    public void removeExpiredNonces(long timestamp) {
+        UsedNonce min = new UsedNonce(timestamp, new String[0]);
+        synchronized (usedNonces) {
+            // Because usedNonces is a TreeSet, its iterator produces
+            // elements from oldest to newest (their natural order).
+            for (Iterator<UsedNonce> iter = usedNonces.iterator(); 
iter.hasNext();) {
+                UsedNonce used = iter.next();
+                if (min.compareTo(used) <= 0) {
+                    break; // all the rest are also new enough
+                }
+                iter.remove(); // too old
+            }
+        }
+    }
+
+    /**
+     * Selected parameters from an OAuth request, in a form suitable for
+     * detecting duplicate requests. The implementation is optimized for the
+     * comparison operations (compareTo, equals and hashCode).
+     * 
+     * @author John Kristian
+     */
+    private static class UsedNonce implements Comparable<UsedNonce> {
+        private static final String ENCODING = "UTF-8";
+
+        private final String sortKey;
+
+        /**
+         * Construct an object containing the given timestamp, nonce and other
+         * parameters. The order of parameters is significant.
+         */
+        UsedNonce(long timestamp, String[] nonceEtc) {
+            StringBuilder key = new StringBuilder(String.format("%20d", 
Long.valueOf(timestamp)));
+            // The blank padding ensures that timestamps are compared as 
numbers.
+            for (String etc : nonceEtc) {
+                key.append("&").append(etc == null ? " " : percentEncode(etc));
+                // A null value is different from "" or any other String.
+            }
+            sortKey = key.toString();
+        }
+
+        private static String percentEncode(String s) {
+            if (s == null) {
+                return "";
+            }
+            try {
+                return URLEncoder.encode(s, ENCODING)
+                        // OAuth encodes some characters differently:
+                    .replace("+", "%20").replace("*", "%2A")
+                        .replace("%7E", "~");
+                // This could be done faster with more hand-crafted code.
+            }
+            catch (UnsupportedEncodingException wow) {
+                throw new RuntimeException(wow.getMessage(), wow);
+            }
+        }
+
+        /**
+         * Determine the relative order of <code>this</code> and
+         * <code>that</code>, as specified by Comparable. The timestamp is most
+         * significant; that is, if the timestamps are different, return 1 or
+         * -1. If <code>this</code> contains only a timestamp (with no nonce
+         * etc.), return -1 or 0. The treatment of the nonce etc. is murky,
+         * although 0 is returned only if they're all equal.
+         */
+        public int compareTo(UsedNonce that) {
+            return (that == null) ? 1 : sortKey.compareTo(that.sortKey);
+        }
+
+        @Override
+        public int hashCode() {
+            return sortKey.hashCode();
+        }
+
+        /**
+         * Return true iff <code>this</code> and <code>that</code> contain 
equal
+         * timestamps, nonce etc., in the same order.
+         */
+        @Override
+        public boolean equals(Object that) {
+            if (that == null)
+                return false;
+            if (that == this)
+                return true;
+            if (that.getClass() != getClass())
+                return false;
+            return sortKey.equals(((UsedNonce) that).sortKey);
+        }
+
+        @Override
+        public String toString() {
+            return sortKey;
+        }
+    }
+}

Added: 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/test/unit/NonceStoreTest.java
==============================================================================
--- (empty file)
+++ 
trunk/amdatu-auth/oauth-stores/nonce-store-mem/src/test/java/org/amdatu/auth/oauth/test/unit/NonceStoreTest.java
    Wed Jul 13 12:56:22 2011
@@ -0,0 +1,96 @@
+/*
+    Copyright (C) 2010 Amdatu.org
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.auth.oauth.test.unit;
+
+import org.amdatu.oauth.store.nonce.mem.service.InMemNonceStorageProviderImpl;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NonceStoreTest {
+    @Test
+    public void test() throws Exception {
+        InMemNonceStorageProviderImpl store = new 
InMemNonceStorageProviderImpl();
+        
+        String nonce = "A";
+        long timestamp1 = 10000; // second 10
+        long timestamp2 = 10001;
+        long timestamp3 = 10002;
+        long timestamp4 = 20000;
+        long timestamp5 = 2435436;
+        
+        // Validate that the same nonce can be added only once
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertTrue(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertTrue(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp1, no nonces should have been 
removed
+        store.removeExpiredNonces(timestamp1);
+        Assert.assertFalse(store.addNonce(timestamp1, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp2, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp2, timestamp1 nonces should have 
been removed
+        store.removeExpiredNonces(timestamp2);
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp2, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp3, timestamp1 and timestamp2 
nonces should have been removed
+        store.removeExpiredNonces(timestamp3);
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertFalse(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp4, timestamp1, timestamp2 and 
timestamp3 nonces should have been removed
+        store.removeExpiredNonces(timestamp4);
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertFalse(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp5, timestamp1, timestamp2, 
timestamp3 and timestamp4 
+        // nonces should have been removed
+        store.removeExpiredNonces(timestamp5);
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertTrue(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertFalse(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+        
+        // Validate nonce removal of timestamp5+1, all nonces should have been 
removed
+        store.removeExpiredNonces(timestamp5+1);
+        Assert.assertTrue(store.addNonce(timestamp1, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp2, nonce, null));
+        Assert.assertTrue(store.addNonce(timestamp3, nonce, new 
String[]{"arg1"}));
+        Assert.assertTrue(store.addNonce(timestamp4, nonce, new 
String[]{"arg2"}));
+        Assert.assertTrue(store.addNonce(timestamp5, nonce, new 
String[]{"arg1", "arg2"}));
+    }
+}

Added: trunk/amdatu-auth/oauth-stores/pom.xml
==============================================================================
--- (empty file)
+++ trunk/amdatu-auth/oauth-stores/pom.xml      Wed Jul 13 12:56:22 2011
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2010, 2011 The Amdatu Foundation
+
+  Licensed 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.verning permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu.auth</groupId>
+    <artifactId>org.amdatu.auth</artifactId>
+    <version>0.2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>org.amdatu.auth.oauth.stores</artifactId>
+  <packaging>pom</packaging>
+  <name>Amdatu Auth - OAuth stores</name>
+  <description>This module holds OAuth storage implementations</description>
+
+  <modules>
+    <module>nonce-store-mem</module>
+  </modules>
+</project>

Modified: 
trunk/amdatu-auth/test-integration/base/src/main/java/org/amdatu/auth/test/integration/base/AuthFixture.java
==============================================================================
--- 
trunk/amdatu-auth/test-integration/base/src/main/java/org/amdatu/auth/test/integration/base/AuthFixture.java
        (original)
+++ 
trunk/amdatu-auth/test-integration/base/src/main/java/org/amdatu/auth/test/integration/base/AuthFixture.java
        Wed Jul 13 12:56:22 2011
@@ -48,6 +48,7 @@
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.oauth.api").versionAsInProject(),
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.oauth.client").versionAsInProject(),
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.oauth.server").versionAsInProject(),
+            
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.oauth.store.nonce.mem").versionAsInProject(),
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.oauth.consumerregistry-fs").versionAsInProject(),
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.login.service").versionAsInProject(),
             
mavenBundle().groupId("org.amdatu.auth").artifactId("org.amdatu.auth.useradmin.rest").versionAsInProject()

Modified: trunk/amdatu-auth/test-integration/tests/pom.xml
==============================================================================
--- trunk/amdatu-auth/test-integration/tests/pom.xml    (original)
+++ trunk/amdatu-auth/test-integration/tests/pom.xml    Wed Jul 13 12:56:22 2011
@@ -147,6 +147,13 @@
         <scope>compile</scope>
         <type>bundle</type>
       </dependency>
+      <dependency>
+        <groupId>org.amdatu.auth</groupId>
+        <artifactId>org.amdatu.auth.oauth.store.nonce.mem</artifactId>
+        <version>${project.version}</version>
+        <scope>test</scope>
+        <type>bundle</type>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
@@ -207,6 +214,11 @@
     </dependency>
     <dependency>
       <groupId>org.amdatu.auth</groupId>
+      <artifactId>org.amdatu.auth.oauth.store.nonce.mem</artifactId>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.amdatu.auth</groupId>
       <artifactId>org.amdatu.auth.login.service</artifactId>
       <type>bundle</type>
     </dependency>
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits

Reply via email to