[jboss-cvs] Picketbox SVN: r388 - in trunk: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers and 2 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Wed Mar 6 02:22:16 EST 2013


Author: pskopek at redhat.com
Date: 2013-03-06 02:22:16 -0500 (Wed, 06 Mar 2013)
New Revision: 388

Added:
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/ExternalPasswordCache.java
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/PasswordCache.java
   trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/ExecPasswordCmd.java
   trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java
Modified:
   trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java
   trunk/security-spi/common/
   trunk/security-spi/common/src/main/java/org/jboss/security/PicketBoxLogger.java
Log:
[SECURITY-729] Adding mechanism to cache passwords obtained from external sources supplied to login modules. Use {EXT} - non-cached, {EXTC[:timeout]} cached with optional expiration in milliseconds.

Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/ExternalPasswordCache.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/ExternalPasswordCache.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/ExternalPasswordCache.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -0,0 +1,133 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+
+package org.jboss.security;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * External command password cache.
+ * Singleton password cache.
+ * 
+ * @author Peter Skopek <pskopek at redhat.com>
+ * @version $Revision:$
+ */
+public class ExternalPasswordCache implements PasswordCache {
+
+   private static final ExternalPasswordCache PASSWORD_CACHE = new ExternalPasswordCache(); 
+
+   private Map<String, PasswordRecord> cache;
+   private MessageDigest md5Digest = null;
+
+   private ExternalPasswordCache() {
+      cache = Collections.synchronizedMap(new HashMap<String, PasswordRecord>());
+      try {
+         md5Digest = MessageDigest.getInstance("MD5");
+      }
+      catch (NoSuchAlgorithmException e) {
+         // Cannot get MD5 algorithm instance for hashing password commands. Using NULL.
+         PicketBoxLogger.LOGGER.errorCannotGetMD5AlgorithmInstance();
+      }
+   }
+
+   public static ExternalPasswordCache getExternalPasswordCacheInstance() {
+      return PASSWORD_CACHE;
+   }
+   
+   /* (non-Javadoc)
+    * @see org.jboss.security.PasswordCache#contains(java.lang.String)
+    */
+   @Override
+   public boolean contains(String key, long timeOut) {
+      String transformedKey = transformKey(key);
+      PasswordRecord pr = cache.get(transformedKey);
+      if (pr != null && (timeOut == 0 || System.currentTimeMillis() - pr.timeOut < timeOut)) {
+         return true;
+      }      
+      return false;
+   }
+
+   /* (non-Javadoc)
+    * @see org.jboss.security.PasswordCache#getPassword(java.lang.String)
+    */
+   @Override
+   public char[] getPassword(String key) {
+      String newKey = transformKey(key);
+      PicketBoxLogger.LOGGER.traceRetrievingPasswordFromCache(newKey);
+      PasswordRecord pr = cache.get(newKey);
+      return pr.password;
+   }
+
+   /* (non-Javadoc)
+    * @see org.jboss.security.PasswordCache#storePassword(java.lang.String, char[])
+    */
+   @Override
+   public void storePassword(String key, char[] password) {
+      String newKey = transformKey(key);
+      PicketBoxLogger.LOGGER.traceStoringPasswordToCache(newKey);
+      PasswordRecord pr = new PasswordRecord();
+      pr.timeOut = System.currentTimeMillis();
+      pr.password = password;
+      cache.put(newKey, pr);
+   }
+   
+   private String transformKey(String key) {
+      String newKey = key;
+      if (md5Digest != null) {
+         md5Digest.reset();
+         byte[] bt = key.getBytes();
+         byte[] md5 = md5Digest.digest(bt);
+         newKey = new String(Base64Utils.tob64(md5));
+      }
+      return newKey;
+   }
+   
+   /**
+    * Get number of cached passwords. 
+    * Mainly for testing purpose.
+    */
+   public int getCachedPasswordsCount() {
+      return cache.size();
+   }
+
+   /* (non-Javadoc)
+    * @see org.jboss.security.PasswordCache#reset()
+    */
+   @Override
+   public void reset() {
+      PicketBoxLogger.LOGGER.traceResettingCache();
+      cache.clear();
+   }
+   
+   
+}
+
+class PasswordRecord {
+
+   long timeOut;
+   char[] password;
+   
+}

Added: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/PasswordCache.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/PasswordCache.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/PasswordCache.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -0,0 +1,61 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.security;
+
+/**
+ * Interface to cache passwords retrieved from external commands.
+ * 
+ * @author Peter Skopek <pskopek at redhat.com>
+ * @version $Revision:$
+ */
+public interface PasswordCache {
+   
+   /**
+    * Checks whether the cache already contains given key. Non zero timeOut will be checked to expire cache entry.  
+    *   
+    * @param key
+    * @param timeOut
+    * @return
+    */
+   public boolean contains(String key, long timeOut);
+   
+   /**
+    * Get password from the cache.
+    * Returns null if there is no such key in the cache.
+    * 
+    * @param key
+    * @return
+    */
+   char[] getPassword(String key); 
+   
+   /**
+    * Store password to the cache.
+    * @param key
+    * @param password
+    */
+   public void storePassword(String key, char[] password);
+
+   /**
+    * Reset the cache (clean whole cache and start all over again).
+    */
+   public void reset();
+}

Modified: trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java	2013-03-01 21:15:12 UTC (rev 387)
+++ trunk/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -33,8 +33,6 @@
 import java.util.ArrayList;
 import java.util.StringTokenizer;
 
-import org.jboss.logging.Logger;
-
 /**
  * Util.
  * 
@@ -44,15 +42,20 @@
  */
 public class Util
 {
+   private static PasswordCache externalPasswordCache;
+   
    /**
     * Execute a password load command to obtain the char[] contents of a
     * password.
     * @param  passwordCmd  - A command to execute to obtain the plaintext
     * password. The format is one of:
     * '{EXT}...' where the '...' is the exact command
+    * '{EXTC[:expiration_in_millis]}...' where the '...' is the exact command
     * line that will be passed to the Runtime.exec(String) method to execute a
     * platform command. The first line of the command output is used as the
     * password.
+    * EXTC variant will cache the passwords for expiration_in_millis milliseconds. 
+    * Default cache expiration is 0 = infinity. 
     * '{CLASS}classname[:ctorargs]' where the '[:ctorargs]' is an optional
     * string delimited by the ':' from the classname that will be passed to the
     * classname ctor. The ctorargs itself is a comma delimited list of strings.
@@ -84,12 +87,36 @@
       if( password == null )
       {
          // Load the password
-         if( passwordCmdType.equals("EXT") )
+         if (passwordCmdType.startsWith("EXTC")) {
+            long timeOut = 0;
+            if (passwordCmdType.indexOf(':') > -1) {
+               try {
+                  String[] token = passwordCmdType.split(":");
+                  timeOut = Long.parseLong(token[1]);
+               } catch (Throwable e) {
+                  // ignore
+               }
+            }
+            if (externalPasswordCache == null) {
+               externalPasswordCache = ExternalPasswordCache
+                     .getExternalPasswordCacheInstance();
+            }
+            if (externalPasswordCache.contains(passwordCmd, timeOut)) {
+               password = externalPasswordCache.getPassword(passwordCmd);
+            } else {
+               password = execPasswordCmd(passwordCmd);
+               if (password != null) {
+                  externalPasswordCache.storePassword(passwordCmd, password);
+               }
+            }
+         } else if (passwordCmdType.startsWith("EXT")) {
+            // non-cached EXT variant
             password = execPasswordCmd(passwordCmd);
-         else if( passwordCmdType.equals("CLASS") )
+         } else if (passwordCmdType.equals("CLASS")) {
             password = invokePasswordClass(passwordCmd);
-         else
+         } else {
             throw PicketBoxMessages.MESSAGES.invalidPasswordCommandType(passwordCmdType);
+         }   
       }
       return password;
    }

Added: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/ExecPasswordCmd.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/ExecPasswordCmd.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/ExecPasswordCmd.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.test.security.helpers;
+
+/**
+ * A test class for the {EXT}... Util.loadPassword method. 
+ * 
+ * @author Scott.Stark at jboss.org
+ * @version $Revision:$
+ */
+public class ExecPasswordCmd
+{
+   public static void main(String[] args)
+   {
+      String password = null;
+      if (args != null && args.length == 1) {
+         password = "password" + args[0];
+      }
+      else if (args != null && args.length == 2) {
+         password = "password" + args[0] + String.valueOf(System.currentTimeMillis());
+      }
+      else {
+         // honor the original value as default
+         password = "password3";
+      }
+
+      System.out.println(password);
+      System.out.flush();
+      
+   }
+}

Added: trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java
===================================================================
--- trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	                        (rev 0)
+++ trunk/security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -0,0 +1,193 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.test.security.helpers;
+
+import junit.framework.TestCase;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Arrays;
+
+import org.jboss.logging.Logger;
+import org.jboss.security.ExternalPasswordCache;
+import org.jboss.security.Util;
+import org.jboss.security.plugins.FilePassword;
+import org.jboss.security.util.StringPropertyReplacer;
+
+/**
+ org.jboss.security.Util tests
+ 
+ @author Scott.Stark at jboss.org
+ @version $Revision: 57211 $
+*/
+public class SecurityUtilUnitTestCase
+   extends TestCase
+{
+   
+   private static Logger log = Logger.getLogger(SecurityUtilUnitTestCase.class);
+   private File tmpPassword;
+   private File password;
+
+   public SecurityUtilUnitTestCase(String name)
+   {
+      super(name);
+   }
+
+   protected void setUp() throws Exception
+   {
+      super.setUp();
+      // Create a tmp password file for testTmpFilePassword
+      tmpPassword = new File(System.getProperty("java.io.tmpdir"), "tmp.password");
+      FileWriter writer = new FileWriter(tmpPassword);
+      writer.write("password1");
+      writer.close();
+
+      // Create the opaque password file for testFilePassword
+      password = new File(System.getProperty("java.io.tmpdir")+ "/tst.password");
+      String[] args2 = {
+         "12345678", // salt
+         "17", // count
+         "password2", // password
+         password.getAbsolutePath() // password-file
+      };
+      FilePassword.main(args2);
+      log.info("Created password file: "+args2[2]);
+   }
+   protected void tearDown() throws Exception
+   {
+      tmpPassword.delete();
+      password.delete();
+      super.tearDown();   
+   }
+
+   /**
+    * Test {CLASS}org.jboss.security.plugins.TmpFilePassword
+    * @throws Exception
+    */
+   public void testTmpFilePassword() throws Exception
+   {
+      String passwordCmd = "{CLASS}org.jboss.security.plugins.TmpFilePassword:${java.io.tmpdir}/tmp.password";
+      passwordCmd = StringPropertyReplacer.replaceProperties(passwordCmd);
+      char[] password = Util.loadPassword(passwordCmd);
+      assertTrue("password1", Arrays.equals(password, "password1".toCharArray()));
+   }
+   /**
+    * Test {CLASS}org.jboss.security.plugins.FilePassword
+    * @throws Exception
+    */
+   public void testFilePassword() throws Exception
+   {
+      String passwordCmd = "{CLASS}org.jboss.security.plugins.FilePassword:${java.io.tmpdir}/tst.password";
+      passwordCmd = StringPropertyReplacer.replaceProperties(passwordCmd);
+      char[] password = Util.loadPassword(passwordCmd);
+      assertTrue("password2", Arrays.equals(password, "password2".toCharArray()));
+   }
+   /**
+    * Test {EXT}org.jboss.test.security.helpers.ExecPasswordCmd
+    * @throws Exception
+    */
+   public void testExtPassword() throws Exception
+   {
+      String passwordCmd = buildExtCommand("EXT");
+      log.info("Executing password command:" + passwordCmd);
+      char[] password = Util.loadPassword(passwordCmd);
+      assertTrue("password3", Arrays.equals(password, "password3".toCharArray()));
+   }
+   
+   public void testExtPasswordCache() throws Exception {
+      
+      // reset ext password the cache
+      ExternalPasswordCache.getExternalPasswordCacheInstance().reset();
+      
+      String passwordCmd = buildExtCommand("EXTC");
+      char[] password = Util.loadPassword(passwordCmd + " 4");
+      assertTrue("password4", Arrays.equals(password, "password4".toCharArray()));
+      char[] cachedPassword = Util.loadPassword(passwordCmd + " 4");
+      assertTrue("password4 cached:1", Arrays.equals(password, "password4".toCharArray()));
+      assertTrue("password4 cached - real call:1", 
+            ExternalPasswordCache.getExternalPasswordCacheInstance().getCachedPasswordsCount() == 1);
+
+      cachedPassword = Util.loadPassword(passwordCmd + " 5");
+      assertTrue("password5", Arrays.equals(cachedPassword, "password5".toCharArray()));
+      cachedPassword = Util.loadPassword(passwordCmd + " 4");
+      assertTrue("password4 cached:2", Arrays.equals(cachedPassword, "password4".toCharArray()));
+      assertTrue("password4 cached - real call:2", 
+            ExternalPasswordCache.getExternalPasswordCacheInstance().getCachedPasswordsCount() == 2);
+      
+      cachedPassword = Util.loadPassword(passwordCmd + " 5");
+      assertTrue("password5 cached:2", Arrays.equals(cachedPassword, "password5".toCharArray()));
+      assertTrue("password5 cached - real call:2", 
+            ExternalPasswordCache.getExternalPasswordCacheInstance().getCachedPasswordsCount() == 2);
+      
+   }
+   
+   public void testExtPasswordCacheTimeOut() throws Exception {
+      
+      // reset ext password the cache
+      ExternalPasswordCache.getExternalPasswordCacheInstance().reset();
+      
+      final String TO = "500";
+      
+      String passwordCmd = buildExtCommand("EXTC:" + TO);
+      char[] password4 = Util.loadPassword(passwordCmd + " 4 timeOut");
+      assertTrue("password4 timeOut = " + TO, Arrays.equals(new String(password4).substring(0,  9).toCharArray(), "password4".toCharArray()));
+
+      char[] cachedPassword4_2 = Util.loadPassword(passwordCmd + " 4 timeOut");
+      assertTrue("password4 timeOut = " + TO + ", cached:1", Arrays.equals(password4, cachedPassword4_2));
+      
+      long WAIT = 800;
+      Thread.sleep(WAIT);
+      char[] cachedPassword4_3 = Util.loadPassword(passwordCmd + " 4 timeOut");
+
+      assertFalse("password4 timeOut = " + TO + " cached:1, wait = " + WAIT, 
+            Arrays.equals(password4, cachedPassword4_3));
+
+      char[] cachedPassword5 = Util.loadPassword(passwordCmd + " 5 timeOut");
+      assertTrue("password5", Arrays.equals(new String(cachedPassword5).substring(0, 9).toCharArray(), "password5".toCharArray()));
+      cachedPassword5 = Util.loadPassword(passwordCmd + " 4 timeOut");
+      assertTrue("password4 cached - real call:2", 
+            ExternalPasswordCache.getExternalPasswordCacheInstance().getCachedPasswordsCount() == 2);
+      
+      cachedPassword5 = Util.loadPassword(passwordCmd + " 5 timeOut");
+      assertTrue("password5 cached - real call:2", 
+            ExternalPasswordCache.getExternalPasswordCacheInstance().getCachedPasswordsCount() == 2);
+      
+   }
+   
+
+   private String buildExtCommand(String extOption) {
+      // First check for java.exe or java as the binary
+      File java = new File(System.getProperty("java.home"), "/bin/java");
+      File javaExe = new File(System.getProperty("java.home"), "/bin/java.exe");
+      String jre;
+      if( java.exists() )
+         jre = java.getAbsolutePath();
+      else
+         jre = javaExe.getAbsolutePath();
+      // Build the command to run this jre
+      String cmd = jre
+      + " -cp "+System.getProperty("java.class.path")
+      + " org.jboss.test.security.helpers.ExecPasswordCmd";
+      
+      return "{" + extOption +"}"+cmd;
+   }
+   
+}


Property changes on: trunk/security-spi/common
___________________________________________________________________
Modified: svn:ignore
   - target
.settings
*.iml
.metadata
.project

   + target
.settings
*.iml
.metadata
.project
.classpath


Modified: trunk/security-spi/common/src/main/java/org/jboss/security/PicketBoxLogger.java
===================================================================
--- trunk/security-spi/common/src/main/java/org/jboss/security/PicketBoxLogger.java	2013-03-01 21:15:12 UTC (rev 387)
+++ trunk/security-spi/common/src/main/java/org/jboss/security/PicketBoxLogger.java	2013-03-06 07:22:16 UTC (rev 388)
@@ -667,4 +667,21 @@
     @LogMessage(level = Logger.Level.INFO)
     @Message(id = 361, value = "Default Security Vault Implementation Initialized and Ready")
     void infoVaultInitialized();
+    
+    @LogMessage(level = Logger.Level.ERROR)
+    @Message(id = 362, value = "Cannot get MD5 algorithm instance for hashing password commands. Using NULL.")
+    void errorCannotGetMD5AlgorithmInstance();
+
+    @LogMessage(level = Logger.Level.TRACE)
+    @Message(id = 363, value = "Retrieving password from the cache for key: %s")
+    void traceRetrievingPasswordFromCache(String newKey);
+
+    @LogMessage(level = Logger.Level.TRACE)
+    @Message(id = 364, value = "Storing password to the cache for key: %s")
+    void traceStoringPasswordToCache(String newKey);
+
+    @LogMessage(level = Logger.Level.TRACE)
+    @Message(id = 365, value = "Resetting cache")
+    void traceResettingCache();
+
 }
\ No newline at end of file



More information about the jboss-cvs-commits mailing list