[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