summaryrefslogtreecommitdiff
path: root/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2012-03-10 19:22:10 +0000
committerRobert Godfrey <rgodfrey@apache.org>2012-03-10 19:22:10 +0000
commit4eaa4e42093e5524d9552d8fa312c214524b6bb4 (patch)
treea251d57ee92d9c779fe4455c583be0ed90e69a43 /qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java
parent92be7e8f3163c048a8642d2deeaa921bbb65dc9c (diff)
downloadqpid-python-4eaa4e42093e5524d9552d8fa312c214524b6bb4.tar.gz
NO-JIRA : AMQP-1-0 sandbox updates - merge from trunkrg-amqp-1-0-sandbox
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rg-amqp-1-0-sandbox@1299257 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java')
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java484
1 files changed, 484 insertions, 0 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java
new file mode 100644
index 0000000000..7088fae50c
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java
@@ -0,0 +1,484 @@
+/*
+ *
+ * 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 org.apache.log4j.Logger;
+import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser;
+import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+
+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.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Pattern;
+
+public abstract class AbstractPasswordFilePrincipalDatabase<U extends PasswordPrincipal> implements PrincipalDatabase
+{
+ private final Pattern _regexp = Pattern.compile(":");
+
+ private final Map<String, AuthenticationProviderInitialiser> _saslServers =
+ new HashMap<String, AuthenticationProviderInitialiser>();
+
+ protected static final String DEFAULT_ENCODING = "utf-8";
+ private final Map<String, U> _userMap = new HashMap<String, U>();
+ private final ReentrantLock _userUpdate = new ReentrantLock();
+ private final Random _random = new Random();
+ private File _passwordFile;
+
+
+ protected AbstractPasswordFilePrincipalDatabase(UsernamePasswordInitialiser... initialisers)
+ {
+ for(UsernamePasswordInitialiser initialiser : initialisers)
+ {
+ initialiser.initialise(this);
+ _saslServers.put(initialiser.getMechanismName(), initialiser);
+ }
+ }
+
+ public final void setPasswordFile(String passwordFile) throws IOException
+ {
+ File f = new File(passwordFile);
+ getLogger().info("PasswordFile using file " + f.getAbsolutePath());
+ _passwordFile = f;
+ if (!f.exists())
+ {
+ throw new FileNotFoundException("Cannot find password file " + f);
+ }
+ if (!f.canRead())
+ {
+ throw new FileNotFoundException("Cannot read password file " + f +
+ ". Check permissions.");
+ }
+
+ loadPasswordFile();
+ }
+
+ /**
+ * SASL Callback Mechanism - sets the Password in the PasswordCallback based on the value in the PasswordFile
+ * If you want to change the password for a user, use updatePassword instead.
+ *
+ * @param principal The Principal to set the password for
+ * @param callback The PasswordCallback to call setPassword on
+ *
+ * @throws javax.security.auth.login.AccountNotFoundException If the Principal cannot be found in this Database
+ */
+ public final void setPassword(Principal principal, PasswordCallback callback) throws AccountNotFoundException
+ {
+ if (_passwordFile == null)
+ {
+ throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
+ }
+ if (principal == null)
+ {
+ throw new IllegalArgumentException("principal must not be null");
+ }
+ char[] pwd = lookupPassword(principal.getName());
+
+ if (pwd != null)
+ {
+ callback.setPassword(pwd);
+ }
+ else
+ {
+ throw new AccountNotFoundException("No account found for principal " + principal);
+ }
+ }
+
+
+ /**
+ * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
+ * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
+ *
+ * @param name The principal name to lookup
+ *
+ * @return a char[] for use in SASL.
+ */
+ protected final char[] lookupPassword(String name)
+ {
+ U user = _userMap.get(name);
+ if (user == null)
+ {
+ return null;
+ }
+ else
+ {
+ return user.getPassword();
+ }
+ }
+
+ protected boolean compareCharArray(char[] a, char[] b)
+ {
+ boolean equal = false;
+ if (a.length == b.length)
+ {
+ equal = true;
+ int index = 0;
+ while (equal && index < a.length)
+ {
+ equal = a[index] == b[index];
+ index++;
+ }
+ }
+ return equal;
+ }
+
+ /**
+ * Changes the password for the specified user
+ *
+ * @param principal to change the password for
+ * @param password plaintext password to set the password too
+ */
+ public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
+ {
+ U user = _userMap.get(principal.getName());
+
+ if (user == null)
+ {
+ throw new AccountNotFoundException(principal.getName());
+ }
+
+ char[] orig = user.getPassword();
+ _userUpdate.lock();
+ try
+ {
+ user.setPassword(password);
+
+ savePasswordFile();
+
+ return true;
+ }
+ catch (IOException e)
+ {
+ getLogger().error("Unable to save password file due to '" + e.getMessage()
+ + "', password change for user '" + principal + "' discarded");
+ //revert the password change
+ user.restorePassword(orig);
+
+ return false;
+ }
+ finally
+ {
+ _userUpdate.unlock();
+ }
+ }
+
+
+ private void loadPasswordFile() throws IOException
+ {
+ try
+ {
+ _userUpdate.lock();
+ _userMap.clear();
+
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ String[] result = _regexp.split(line);
+ if (result == null || result.length < 2 || result[0].startsWith("#"))
+ {
+ continue;
+ }
+
+ U user = createUserFromFileData(result);
+ getLogger().info("Created user:" + user);
+ _userMap.put(user.getName(), user);
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ }
+ finally
+ {
+ _userUpdate.unlock();
+ }
+ }
+
+ protected abstract U createUserFromFileData(String[] result);
+
+
+ protected abstract Logger getLogger();
+
+ protected File createTempFileOnSameFilesystem()
+ {
+ File liveFile = _passwordFile;
+ File tmp;
+
+ do
+ {
+ tmp = new File(liveFile.getPath() + _random.nextInt() + ".tmp");
+ }
+ while(tmp.exists());
+
+ tmp.deleteOnExit();
+ return tmp;
+ }
+
+ protected void swapTempFileToLive(final File temp) throws IOException
+ {
+ File live = _passwordFile;
+ // Remove any existing ".old" file
+ final File old = new File(live.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ // Create an new ".old" file
+ if(!live.renameTo(old))
+ {
+ //unable to rename the existing file to the backup name
+ getLogger().error("Could not backup the existing password file");
+ throw new IOException("Could not backup the existing password file");
+ }
+
+ // Move temp file to be the new "live" file
+ if(!temp.renameTo(live))
+ {
+ //failed to rename the new file to the required filename
+ if(!old.renameTo(live))
+ {
+ //unable to return the backup to required filename
+ getLogger().error(
+ "Could not rename the new password file into place, and unable to restore original file");
+ throw new IOException("Could not rename the new password file into place, and unable to restore original file");
+ }
+
+ getLogger().error("Could not rename the new password file into place");
+ throw new IOException("Could not rename the new password file into place");
+ }
+ }
+
+ protected void savePasswordFile() throws IOException
+ {
+ try
+ {
+ _userUpdate.lock();
+
+ BufferedReader reader = null;
+ PrintStream writer = null;
+
+ File tmp = createTempFileOnSameFilesystem();
+
+ try
+ {
+ writer = new PrintStream(tmp);
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ String[] result = _regexp.split(line);
+ if (result == null || result.length < 2 || result[0].startsWith("#"))
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ continue;
+ }
+
+ U user = _userMap.get(result[0]);
+
+ if (user == null)
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ }
+ else if (!user.isDeleted())
+ {
+ if (!user.isModified())
+ {
+ writer.write(line.getBytes(DEFAULT_ENCODING));
+ writer.println();
+ }
+ else
+ {
+ byte[] encodedPassword = user.getEncodedPassword();
+
+ writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
+ writer.write(encodedPassword);
+ writer.println();
+
+ user.saved();
+ }
+ }
+ }
+
+ for (U user : _userMap.values())
+ {
+ if (user.isModified())
+ {
+ byte[] encodedPassword;
+ encodedPassword = user.getEncodedPassword();
+ writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
+ writer.write(encodedPassword);
+ writer.println();
+ user.saved();
+ }
+ }
+ }
+ catch(IOException e)
+ {
+ getLogger().error("Unable to create the new password file: " + e);
+ throw new IOException("Unable to create the new password file",e);
+ }
+ finally
+ {
+
+ try
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+
+ }
+
+ swapTempFileToLive(tmp);
+ }
+ finally
+ {
+ _userUpdate.unlock();
+ }
+ }
+
+ protected abstract U createUserFromPassword(Principal principal, char[] passwd);
+
+
+ public void reload() throws IOException
+ {
+ loadPasswordFile();
+ }
+
+ public Map<String, AuthenticationProviderInitialiser> getMechanisms()
+ {
+ return _saslServers;
+ }
+
+ public List<Principal> getUsers()
+ {
+ return new LinkedList<Principal>(_userMap.values());
+ }
+
+ public Principal getUser(String username)
+ {
+ if (_userMap.containsKey(username))
+ {
+ return new UsernamePrincipal(username);
+ }
+ return null;
+ }
+
+ public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
+ {
+ U user = _userMap.get(principal.getName());
+
+ if (user == null)
+ {
+ throw new AccountNotFoundException(principal.getName());
+ }
+
+ try
+ {
+ _userUpdate.lock();
+ user.delete();
+
+ try
+ {
+ savePasswordFile();
+ }
+ catch (IOException e)
+ {
+ getLogger().error("Unable to remove user '" + user.getName() + "' from password file.");
+ return false;
+ }
+
+ _userMap.remove(user.getName());
+ }
+ finally
+ {
+ _userUpdate.unlock();
+ }
+
+ return true;
+ }
+
+ public boolean createPrincipal(Principal principal, char[] password)
+ {
+ if (_userMap.get(principal.getName()) != null)
+ {
+ return false;
+ }
+
+ U user = createUserFromPassword(principal, password);
+
+
+ try
+ {
+ _userUpdate.lock();
+ _userMap.put(user.getName(), user);
+
+ try
+ {
+ savePasswordFile();
+ return true;
+ }
+ catch (IOException e)
+ {
+ //remove the use on failure.
+ _userMap.remove(user.getName());
+ return false;
+ }
+ }
+ finally
+ {
+ _userUpdate.unlock();
+ }
+ }
+}