You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2007/10/29 19:05:02 UTC

svn commit: r589775 - in /directory/apacheds/branches/bigbang/kerberos-shared/src: main/java/org/apache/directory/server/kerberos/shared/replay/ test/java/org/apache/directory/server/kerberos/shared/replay/

Author: elecharny
Date: Mon Oct 29 11:04:59 2007
New Revision: 589775

URL: http://svn.apache.org/viewvc?rev=589775&view=rev
Log:
Refactored the replay cache :
- we now use a thread to clean the cache 
- the entries are stored into a hashMap to speedup the cleaning
- the key is the clientprincipal
- for each clientPrincipal, we store a list of entries
- when the cleaning thread is running, we remove from the beginning of the list all the timed out entries

Added:
    directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/
    directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java
Modified:
    directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java
    directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java

Modified: directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java?rev=589775&r1=589774&r2=589775&view=diff
==============================================================================
--- directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java (original)
+++ directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCache.java Mon Oct 29 11:04:59 2007
@@ -21,8 +21,11 @@
 
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.security.auth.kerberos.KerberosPrincipal;
 
@@ -33,61 +36,37 @@
  * "The replay cache will store at least the server name, along with the client name,
  * time, and microsecond fields from the recently-seen authenticators, and if a
  * matching tuple is found, the KRB_AP_ERR_REPEAT error is returned."
+ * 
+ * We will store the entries using an HashMap which key will be the client
+ * principal, and we will store a list of entries for each client principal.
+ * 
+ * A thread will run every N seconds to clean the cache from entries out of the 
+ * clockSkew
  *    
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$
  */
-public class InMemoryReplayCache implements ReplayCache
+public class InMemoryReplayCache extends Thread implements ReplayCache
 {
-    private List<ReplayCacheEntry> list = new ArrayList<ReplayCacheEntry>();
-
-    private long clockSkew = 5 * KerberosTime.MINUTE;
+    /** Stores the entries in memory */
+    private Map<KerberosPrincipal, List<ReplayCacheEntry>> cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
 
+    /** default clock skew */
+    private static final long DEFAULT_CLOCK_SKEW = 5 * KerberosTime.MINUTE;
+    
+    /** The clock skew */
+    private long clockSkew = DEFAULT_CLOCK_SKEW;
+
+    /** The default delay between each run of the cleaning process : 5 s */
+    private static long DEFAULT_DELAY = 5 * 1000;  
+    
+    /** The delay to wait between each cache cleaning */
+    private long delay;
 
     /**
-     * Sets the clock skew.
-     *
-     * @param clockSkew
+     * A structure to hold an entry
      */
-    public void setClockSkew( long clockSkew )
-    {
-        this.clockSkew = clockSkew;
-    }
-
-
-    public synchronized boolean isReplay( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
-        KerberosTime clientTime, int clientMicroSeconds )
-    {
-        ReplayCacheEntry testEntry = new ReplayCacheEntry( serverPrincipal, clientPrincipal, clientTime,
-            clientMicroSeconds );
-
-        Iterator<ReplayCacheEntry> it = list.iterator();
-        while ( it.hasNext() )
-        {
-            ReplayCacheEntry entry = it.next();
-
-            if ( entry.equals( testEntry ) )
-            {
-                return true;
-            }
-
-            if ( entry.isOutsideClockSkew( clockSkew ) )
-            {
-                it.remove();
-            }
-        }
-
-        return false;
-    }
-
-
-    public synchronized void save( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
-        KerberosTime clientTime, int clientMicroSeconds )
-    {
-        list.add( new ReplayCacheEntry( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) );
-    }
-
-    private class ReplayCacheEntry
+    public class ReplayCacheEntry
     {
         private KerberosPrincipal serverPrincipal;
         private KerberosPrincipal clientPrincipal;
@@ -137,6 +116,200 @@
         public boolean isOutsideClockSkew( long clockSkew )
         {
             return !clientTime.isInClockSkew( clockSkew );
+        }
+    }
+
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds.
+     */
+    public InMemoryReplayCache()
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        delay = DEFAULT_DELAY;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds. Sets the
+     * clockSkew to the given value
+     * 
+     * @param clockSkew the allowed skew (milliseconds)
+     */
+    public InMemoryReplayCache( long clockSkew )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        delay = DEFAULT_DELAY;
+        this.clockSkew = clockSkew;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * clockSkew to the given value, and set the cleaning thread 
+     * kick off delay
+     * 
+     * @param clockSkew the allowed skew (milliseconds)
+     * @param delay the interval between each run of the cache 
+     * cleaning thread (milliseconds)
+     */
+    public InMemoryReplayCache( long clockSkew, int delay  )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        this.delay = (long)delay;
+        this.clockSkew = clockSkew;
+        this.start();
+    }
+    
+    
+    /**
+     * Creates a new instance of InMemoryReplayCache. Sets the
+     * delay between each cleaning run to 5 seconds. Sets the 
+     * cleaning thread kick off delay
+     * 
+     * @param delay the interval between each run of the cache 
+     * cleaning thread (milliseconds).
+     */
+    public InMemoryReplayCache( int delay )
+    {
+        cache = new HashMap<KerberosPrincipal, List<ReplayCacheEntry>>();
+        this.delay = (long)delay;
+        this.clockSkew = DEFAULT_CLOCK_SKEW;
+    }
+    
+    
+    /**
+     * Sets the clock skew.
+     *
+     * @param clockSkew
+     */
+    public void setClockSkew( long clockSkew )
+    {
+        this.clockSkew = clockSkew;
+    }
+
+    
+    /**
+     * Set the delay between each cleaning thread run.
+     *
+     * @param delay delay in milliseconds
+     */
+    public void setDelay( long delay )
+    {
+        this.delay = delay;
+    }
+
+    /**
+     * Check if an entry is a replay or not.
+     */
+    public synchronized boolean isReplay( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
+        KerberosTime clientTime, int clientMicroSeconds )
+    {
+        List<ReplayCacheEntry> entries = cache.get( clientPrincipal );
+        
+        if ( ( entries == null ) || ( entries.size() == 0 ) )
+        {
+            return false;
+        }
+        
+        for ( ReplayCacheEntry entry:entries )
+        {
+            if ( serverPrincipal.equals( entry.serverPrincipal ) && 
+                 clientTime.equals( entry.clientTime ) && 
+                 (clientMicroSeconds == entry.clientMicroSeconds ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Add a new entry into the cache. A thread will clean all the timed out
+     * entries.
+     */
+    public synchronized void save( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal,
+        KerberosTime clientTime, int clientMicroSeconds )
+    {
+        List<ReplayCacheEntry> entries = cache.get( clientPrincipal );
+        
+        if ( entries == null )
+        {
+            entries = new ArrayList<ReplayCacheEntry>();
+        }
+        
+        entries.add( new ReplayCacheEntry( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) );
+        
+        cache.put( clientPrincipal, entries );
+    }
+
+    
+    public Map<KerberosPrincipal, List<ReplayCacheEntry>> getCache()
+    {
+        return cache;
+    }
+    
+    /**
+     * A method to remove all the expired entries from the cache.
+     */
+    private synchronized void cleanCache()
+    {
+        Collection<List<ReplayCacheEntry>> entryList = cache.values();
+        
+        if ( ( entryList == null ) || ( entryList.size() == 0 ) )
+        {
+            return;
+        }
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                ReplayCacheEntry entry = iterator.next();
+                
+                if ( entry.isOutsideClockSkew( clockSkew ) )
+                {
+                    iterator.remove();
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+    }
+    
+    
+    /** 
+     * The cleaning thread. It runs every N seconds.
+     */
+    public void run()
+    {
+        while ( true )
+        {
+            try
+            {
+                Thread.sleep( delay );
+                
+                cleanCache();
+            }
+            catch ( InterruptedException ie )
+            {
+                return;
+            }
         }
     }
 }

Modified: directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java?rev=589775&r1=589774&r2=589775&view=diff
==============================================================================
--- directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java (original)
+++ directory/apacheds/branches/bigbang/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/replay/ReplayCache.java Mon Oct 29 11:04:59 2007
@@ -39,10 +39,10 @@
      * Returns whether a request is a replay, based on the server principal, client
      * principal, time, and microseconds.
      * 
-     * @param serverPrincipal 
-     * @param clientPrincipal
-     * @param clientTime
-     * @param clientMicroSeconds 
+     * @param serverPrincipal The server principal 
+     * @param clientPrincipal The client principal
+     * @param clientTime The client time
+     * @param clientMicroSeconds The client microsecond
      * @return true if the request is a replay.
      */
     boolean isReplay( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
@@ -53,10 +53,10 @@
      * Saves the server principal, client principal, time, and microseconds to
      * the replay cache.
      *
-     * @param serverPrincipal 
-     * @param clientPrincipal
-     * @param clientTime
-     * @param clientMicroSeconds 
+     * @param serverPrincipal The server principal 
+     * @param clientPrincipal The client principal
+     * @param clientTime The client time
+     * @param clientMicroSeconds The client microsecond
      */
     void save( KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
         int clientMicroSeconds );

Added: directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java?rev=589775&view=auto
==============================================================================
--- directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java (added)
+++ directory/apacheds/branches/bigbang/kerberos-shared/src/test/java/org/apache/directory/server/kerberos/shared/replay/InMemoryReplayCacheTest.java Mon Oct 29 11:04:59 2007
@@ -0,0 +1,131 @@
+/*
+ *  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.directory.server.kerberos.shared.replay;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
+import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache.ReplayCacheEntry;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test the InMemory replay cache
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev: 542147 $, $Date: 2007-05-28 10:14:21 +0200 (Mon, 28 May 2007) $
+ */
+public class InMemoryReplayCacheTest
+{
+    /**
+     * Test that the cache is working well. We will create a new entry
+     * every 20 ms, with 10 different serverPrincipals.
+     * 
+     * After this period of time, we should only have 25 entries in the cache
+     */
+    @Test
+    public void testCacheSetting() throws Exception
+    {
+        int delay = 500;
+        long clockSkew = 100;
+        
+        // Set a delay of 500 ms and a clock skew of 100 ms
+        InMemoryReplayCache cache = new InMemoryReplayCache( clockSkew, delay );
+        
+        // Loop for 2 seconds, then check that the cache is clean
+        int i = 0;
+        int nbClient = 20;
+        int nbServer = 10;
+        
+        // Inject 100 entries, one every 20 ms
+        while ( i < 100 )
+        {
+            KerberosPrincipal serverPrincipal = new KerberosPrincipal( "server" + i%nbServer + "@APACHE.ORG", PrincipalNameType.KRB_NT_PRINCIPAL.getOrdinal() );
+            KerberosPrincipal clientPrincipal = new KerberosPrincipal( "client" + i%nbClient + "@APACHE.ORG", PrincipalNameType.KRB_NT_PRINCIPAL.getOrdinal() );
+            
+            cache.save( serverPrincipal, clientPrincipal, new KerberosTime( System.currentTimeMillis() ), 0 );
+            
+            Thread.sleep( 20 );
+            i++;
+        }
+        
+        Map<KerberosPrincipal, List<ReplayCacheEntry>> map = cache.getCache();
+
+        // We should have 20 List of entries, as we have injected 20 different
+        // clientPrincipals
+        assertEquals( nbClient, map.size() );
+        
+        int nbEntries = 0;
+        
+        // Loop into the cache to see how many entries we have
+        Collection<List<ReplayCacheEntry>> entryList = map.values();
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                iterator.next();
+                nbEntries ++;
+            }
+        }
+
+        // We should have some
+        assertNotNull( nbEntries );
+        
+        // Wait another delay, so that the cleaning thread will be kicked off
+        Thread.sleep( delay + 50 );
+        
+        nbEntries = 0;
+        
+        for ( List<ReplayCacheEntry> entries:entryList )
+        {
+            if ( ( entries == null ) || ( entries.size() == 0 ) )
+            {
+                continue;
+            }
+            
+            Iterator<ReplayCacheEntry> iterator = entries.iterator();
+            
+            while ( iterator.hasNext() )
+            {
+                iterator.next();
+                nbEntries ++;
+            }
+        }
+
+        // We should not have anymore entry in the cache
+        assertEquals( 0, nbEntries );
+    }
+}