summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ritchie <ritchiem@apache.org>2008-12-05 17:11:08 +0000
committerMartin Ritchie <ritchiem@apache.org>2008-12-05 17:11:08 +0000
commit93d7a8b0e385c703a175a99bc7795c9cb541d355 (patch)
treea7ef68a86396ec31e403bf2acbcbbf9acc8bf727
parent134b171215d8a8cb648d75d64d25b6f420e90a2f (diff)
downloadqpid-python-93d7a8b0e385c703a175a99bc7795c9cb541d355.tar.gz
QPID-1503 : Update to Base64 to correctly save password file. Pulled User out of Base64MD5 to allow better testing. Added unit test case for Base64 and User
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@723792 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java134
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java325
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/UserTest.java131
3 files changed, 466 insertions, 124 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
index 348bccb4e9..86a0b7d961 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java
@@ -21,30 +21,26 @@
package org.apache.qpid.server.security.auth.database;
import org.apache.log4j.Logger;
+import org.apache.qpid.server.security.access.management.AMQUserManagementMBean;
import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser;
-import org.apache.qpid.server.security.access.management.AMQUserManagementMBean;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.EncoderException;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.AccountNotFoundException;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.BufferedReader;
import java.io.FileReader;
-import java.io.UnsupportedEncodingException;
+import java.io.IOException;
import java.io.PrintStream;
-import java.util.regex.Pattern;
-import java.util.Map;
+import java.security.Principal;
import java.util.HashMap;
-import java.util.List;
import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
-import java.security.Principal;
-import java.security.NoSuchAlgorithmException;
+import java.util.regex.Pattern;
/**
* Represents a user database where the account information is stored in a simple flat file.
@@ -64,7 +60,7 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
private Map<String, AuthenticationProviderInitialiser> _saslServers;
AMQUserManagementMBean _mbean;
- private static final String DEFAULT_ENCODING = "utf-8";
+ public static final String DEFAULT_ENCODING = "utf-8";
private Map<String, User> _users = new HashMap<String, User>();
private ReentrantLock _userUpdate = new ReentrantLock();
@@ -284,7 +280,6 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
return true;
}
-
public Map<String, AuthenticationProviderInitialiser> getMechanisms()
{
return _saslServers;
@@ -325,7 +320,6 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
}
}
-
private void loadPasswordFile() throws IOException
{
try
@@ -382,6 +376,7 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
{
tmp.delete();
}
+
try
{
writer = new PrintStream(tmp);
@@ -394,6 +389,7 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
if (result == null || result.length < 2 || result[0].startsWith("#"))
{
writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
continue;
}
@@ -485,114 +481,4 @@ public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
}
}
- private class User implements Principal
- {
- String _name;
- char[] _password;
- byte[] _encodedPassword = null;
- private boolean _modified = false;
- private boolean _deleted = false;
-
- User(String[] data) throws UnsupportedEncodingException
- {
- if (data.length != 2)
- {
- throw new IllegalArgumentException("User Data should be lenght 2, username, password");
- }
-
- _name = data[0];
-
- byte[] encoded_password = data[1].getBytes(DEFAULT_ENCODING);
-
- Base64 b64 = new Base64();
- byte[] decoded = b64.decode(encoded_password);
-
- _encodedPassword = encoded_password;
-
- _password = new char[decoded.length];
-
- int index = 0;
- for (byte c : decoded)
- {
- _password[index++] = (char) c;
- }
- }
-
- public User(String name, char[] password)
- {
- _name = name;
- setPassword(password);
- }
-
- public String getName()
- {
- return _name;
- }
-
- public String toString()
- {
- if (_logger.isDebugEnabled())
- {
- return getName() + ((_encodedPassword == null) ? "" : ":" + new String(_encodedPassword));
- }
- else
- {
- return _name;
- }
- }
-
- char[] getPassword()
- {
- return _password;
- }
-
- void setPassword(char[] password)
- {
- _password = password;
- _modified = true;
- _encodedPassword = null;
- }
-
-
- byte[] getEncodePassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException
- {
- if (_encodedPassword == null)
- {
- encodePassword();
- }
- return _encodedPassword;
- }
-
- private void encodePassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException
- {
- byte[] byteArray = new byte[_password.length];
- int index = 0;
- for (char c : _password)
- {
- byteArray[index++] = (byte) c;
- }
- _encodedPassword = (new Base64()).encode(byteArray);
- }
-
- public boolean isModified()
- {
- return _modified;
- }
-
- public boolean isDeleted()
- {
- return _deleted;
- }
-
- public void delete()
- {
- _deleted = true;
- }
-
- public void saved()
- {
- _modified = false;
- }
-
- }
}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java
new file mode 100644
index 0000000000..5a74160b95
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java
@@ -0,0 +1,325 @@
+/*
+ *
+ * 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.qpid.server.security.auth.database;
+
+import junit.framework.TestCase;
+
+import javax.security.auth.login.AccountNotFoundException;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class Base64MD5PasswordFilePrincipalDatabaseTest extends TestCase
+{
+
+ Base64MD5PasswordFilePrincipalDatabase _database;
+ private String QPID_HOME;
+ private static final String TEST_COMMENT = "# Test Comment";
+ private String USERNAME = "testUser";
+ private static final String TEST_FILE_NAME = "B64Test.tmp";
+
+ public void setUp()
+ {
+ _database = new Base64MD5PasswordFilePrincipalDatabase();
+
+ QPID_HOME = System.getProperty("QPID_HOME");
+
+ assertNotNull("QPID_HOME not set", QPID_HOME);
+ }
+
+ public void tearDown()
+ {
+ File testFile = new File(QPID_HOME + File.separator + TEST_FILE_NAME);
+ if (testFile.exists())
+ {
+ testFile.delete();
+ }
+
+ testFile = new File(QPID_HOME + File.separator + TEST_FILE_NAME + ".old");
+ if (testFile.exists())
+ {
+ testFile.delete();
+ }
+ }
+
+ private File createPasswordFile(int commentLines, int users)
+ {
+ try
+ {
+ File testFile = new File(QPID_HOME + File.separator + TEST_FILE_NAME);
+ if (testFile.exists())
+ {
+ testFile.delete();
+ }
+ testFile.deleteOnExit();
+
+ BufferedWriter writer = new BufferedWriter(new FileWriter(testFile));
+
+ for (int i = 0; i < commentLines; i++)
+ {
+ writer.write(TEST_COMMENT);
+ writer.newLine();
+ }
+
+ for (int i = 0; i < users; i++)
+ {
+ writer.write(USERNAME + i + ":Password");
+ writer.newLine();
+ }
+
+ writer.flush();
+ writer.close();
+
+ return testFile;
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to create test password file." + e.getMessage());
+ }
+
+ return null;
+ }
+
+ private void loadPasswordFile(File file)
+ {
+ try
+ {
+ _database.setPasswordFile(file.toString());
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+ }
+
+ /** **** Test Methods ************** */
+
+ public void testCreatePrincipal()
+ {
+ File testFile = createPasswordFile(1, 0);
+
+ loadPasswordFile(testFile);
+
+ final String CREATED_PASSWORD = "createdPassword";
+ final String CREATED_USERNAME = "createdUser";
+
+ Principal principal = new Principal()
+ {
+ public String getName()
+ {
+ return CREATED_USERNAME;
+ }
+ };
+
+ assertTrue("New user not created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray()));
+
+ loadPasswordFile(testFile);
+
+ assertNotNull("Created User was not saved", _database.getUser(CREATED_USERNAME));
+
+ assertFalse("Duplicate user created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray()));
+
+ testFile.delete();
+ }
+
+ public void testDeletePrincipal()
+ {
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser(USERNAME + "0");
+ assertNotNull("Generated user not present.", user);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail("User should be present" + e.getMessage());
+ }
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ loadPasswordFile(testFile);
+
+ try
+ {
+ _database.deletePrincipal(user);
+ fail("User should not be present");
+ }
+ catch (AccountNotFoundException e)
+ {
+ //pass
+ }
+
+ assertNull("Deleted user still present.", _database.getUser(USERNAME + "0"));
+
+ testFile.delete();
+ }
+
+ public void testGetUsers()
+ {
+ int USER_COUNT = 10;
+ File testFile = createPasswordFile(1, USER_COUNT);
+
+ loadPasswordFile(testFile);
+
+ Principal user = _database.getUser("MISSING_USERNAME");
+ assertNull("Missing user present.", user);
+
+ List<Principal> users = _database.getUsers();
+
+ assertNotNull("Users list is null.", users);
+
+ assertEquals(USER_COUNT, users.size());
+
+ boolean[] verify = new boolean[USER_COUNT];
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ Principal principal = users.get(i);
+
+ assertNotNull("Generated user not present.", principal);
+
+ String name = principal.getName();
+
+ int id = Integer.parseInt(name.substring(USERNAME.length()));
+
+ assertFalse("Duplicated username retrieve", verify[id]);
+ verify[id] = true;
+ }
+
+ for (int i = 0; i < USER_COUNT; i++)
+ {
+ assertTrue("User " + i + " missing", verify[i]);
+ }
+
+ testFile.delete();
+ }
+
+ public void testUpdatePasswordIsSavedToFile()
+ {
+
+ File testFile = createPasswordFile(1, 1);
+
+ loadPasswordFile(testFile);
+
+ Principal testUser = _database.getUser(USERNAME + "0");
+
+ assertNotNull(testUser);
+
+ String NEW_PASSWORD = "NewPassword";
+ String NEW_PASSWORD_HASH = "TmV3UGFzc3dvcmQ=";
+ try
+ {
+ _database.updatePassword(testUser, NEW_PASSWORD.toCharArray());
+ }
+ catch (AccountNotFoundException e)
+ {
+ fail(e.toString());
+ }
+
+ try
+ {
+ BufferedReader reader = new BufferedReader(new FileReader(testFile));
+
+ assertTrue("File has no content", reader.ready());
+
+ assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine());
+
+ assertTrue("File is missing user data.", reader.ready());
+
+ String userLine = reader.readLine();
+
+ String[] result = Pattern.compile(":").split(userLine);
+
+ assertEquals("User line not complete '" + userLine + "'", 2, result.length);
+
+ assertEquals("Username not correct,", USERNAME + "0", result[0]);
+ assertEquals("New Password not correct,", NEW_PASSWORD_HASH, result[1]);
+
+ assertFalse("File has more content", reader.ready());
+
+ }
+ catch (IOException e)
+ {
+ fail("Unable to valdate file contents due to:" + e.getMessage());
+ }
+ testFile.delete();
+ }
+
+ public void testSetPasswordWithMissingFile()
+ {
+ try
+ {
+ _database.setPasswordFile("DoesntExist");
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage(), fnfe.getMessage().startsWith("Cannot find password file"));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+
+ }
+
+ public void testSetPasswordWithReadOnlyFile()
+ {
+
+ File testFile = createPasswordFile(0, 0);
+
+ testFile.setReadOnly();
+
+ try
+ {
+ _database.setPasswordFile(testFile.toString());
+ }
+ catch (FileNotFoundException fnfe)
+ {
+ assertTrue(fnfe.getMessage().startsWith("Cannot read password file "));
+ }
+ catch (IOException e)
+ {
+ fail("Password File was not created." + e.getMessage());
+ }
+
+ testFile.delete();
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/UserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/UserTest.java
new file mode 100644
index 0000000000..99902ef4c5
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/UserTest.java
@@ -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.qpid.server.security.auth.database;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import java.io.UnsupportedEncodingException;
+
+/*
+ Note User is mainly tested by Base64MD5PFPDTest this is just to catch the extra methods
+ */
+public class UserTest extends TestCase
+{
+
+ String USERNAME = "username";
+ String PASSWORD = "password";
+ String HASHED_PASSWORD = "cGFzc3dvcmQ=";
+
+ public void testToLongArrayConstructor()
+ {
+ try
+ {
+ User user = new User(new String[]{USERNAME, PASSWORD, USERNAME});
+ fail("Error expected");
+ }
+ catch (IllegalArgumentException e)
+ {
+ assertEquals("User Data should be length 2, username, password", e.getMessage());
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testArrayConstructor()
+ {
+ try
+ {
+ User user = new User(new String[]{USERNAME, HASHED_PASSWORD});
+ assertEquals("Username incorrect", USERNAME, user.getName());
+ int index = 0;
+
+ char[] hash = HASHED_PASSWORD.toCharArray();
+
+ try
+ {
+ for (byte c : user.getEncodePassword())
+ {
+ assertEquals("Password incorrect", hash[index], (char) c);
+ index++;
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+
+ hash = PASSWORD.toCharArray();
+
+ index=0;
+ for (char c : user.getPassword())
+ {
+ assertEquals("Password incorrect", hash[index], c);
+ index++;
+ }
+
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testToString()
+ {
+
+ User user = new User(USERNAME, PASSWORD.toCharArray());
+
+ // Test logger debug case
+ Logger.getLogger(User.class).setLevel(Level.DEBUG);
+
+ assertEquals("User toString encoding not as expected", USERNAME, user.toString());
+
+ try
+ {
+ char[] hash = HASHED_PASSWORD.toCharArray();
+ int index = 0;
+ for (byte c : user.getEncodePassword())
+ {
+
+ assertEquals("Hash not as expected", hash[index], (char) c);
+ index++;
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+
+ assertEquals("User toString encoding not as expected", USERNAME + ":" + HASHED_PASSWORD,
+ user.toString());
+
+ Logger.getLogger(User.class).setLevel(Level.INFO);
+
+ // Test normal case
+ assertEquals("User toString encoding not as expected", USERNAME, user.toString());
+ }
+
+}
+