diff options
Diffstat (limited to 'qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java')
-rw-r--r-- | qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java | 334 |
1 files changed, 290 insertions, 44 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java index 9da954d74f..5e4678a63b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java @@ -34,11 +34,13 @@ 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.concurrent.locks.ReentrantLock; import java.util.regex.Pattern; /** @@ -50,13 +52,18 @@ import java.util.regex.Pattern; */ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase { + public static final String DEFAULT_ENCODING = "utf-8"; + private static final Logger _logger = Logger.getLogger(PlainPasswordFilePrincipalDatabase.class); - protected File _passwordFile; + private File _passwordFile; - protected Pattern _regexp = Pattern.compile(":"); + private Pattern _regexp = Pattern.compile(":"); - protected Map<String, AuthenticationProviderInitialiser> _saslServers; + private Map<String, AuthenticationProviderInitialiser> _saslServers; + + private Map<String, PlainUser> _users = new HashMap<String, PlainUser>(); + private ReentrantLock _userUpdate = new ReentrantLock(); public PlainPasswordFilePrincipalDatabase() { @@ -83,7 +90,7 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase _saslServers.put(cram.getMechanismName(), cram); } - public void setPasswordFile(String passwordFile) throws FileNotFoundException + public void setPasswordFile(String passwordFile) throws IOException { File f = new File(passwordFile); _logger.info("PlainPasswordFile using file " + f.getAbsolutePath()); @@ -97,10 +104,20 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase throw new FileNotFoundException("Cannot read password file " + f + ". Check permissions."); } + + loadPasswordFile(); } - public void setPassword(Principal principal, PasswordCallback callback) throws IOException, - AccountNotFoundException + /** + * 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 AccountNotFoundException If the Principal cannot be found in this Database + */ + public void setPassword(Principal principal, PasswordCallback callback) throws AccountNotFoundException { if (_passwordFile == null) { @@ -111,6 +128,7 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase throw new IllegalArgumentException("principal must not be null"); } char[] pwd = lookupPassword(principal.getName()); + if (pwd != null) { callback.setPassword(pwd); @@ -121,33 +139,151 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase } } + /** + * Used to verify that the presented Password is correct. Currently only used by Management Console + * + * @param principal The principal to authenticate + * @param password The plaintext password to check + * + * @return true if password is correct + * + * @throws AccountNotFoundException if the principal cannot be found + */ public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException { - try - { - char[] pwd = lookupPassword(principal); - return compareCharArray(pwd, password); - } - catch (IOException e) + char[] pwd = lookupPassword(principal); + + if (pwd == null) { - return false; + throw new AccountNotFoundException("Unable to lookup the specfied users password"); } + + return compareCharArray(pwd, password); + } + /** + * 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 { - return false; // updates denied + PlainUser user = _users.get(principal.getName()); + + if (user == null) + { + throw new AccountNotFoundException(principal.getName()); + } + + try + { + try + { + _userUpdate.lock(); + char[] orig = user.getPassword(); + user.setPassword(password); + + try + { + savePasswordFile(); + } + catch (IOException e) + { + _logger.error("Unable to save password file, password change for user '" + principal + "' discarded"); + //revert the password change + user.setPassword(orig); + return false; + } + return true; + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + catch (Exception e) + { + return false; + } } public boolean createPrincipal(Principal principal, char[] password) { - return false; // updates denied + if (_users.get(principal.getName()) != null) + { + return false; + } + + PlainUser user = new PlainUser(principal.getName(), password); + + try + { + _userUpdate.lock(); + _users.put(user.getName(), user); + + try + { + savePasswordFile(); + return true; + } + catch (IOException e) + { + //remove the use on failure. + _users.remove(user.getName()); + _logger.warn("Unable to create user '" + user.getName()); + return false; + } + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } } public boolean deletePrincipal(Principal principal) throws AccountNotFoundException { - return false; // updates denied + PlainUser user = _users.get(principal.getName()); + + if (user == null) + { + throw new AccountNotFoundException(principal.getName()); + } + + try + { + _userUpdate.lock(); + user.delete(); + + try + { + savePasswordFile(); + } + catch (IOException e) + { + _logger.error("Unable to remove user '" + user.getName() + "' from password file."); + return false; + } + + _users.remove(user.getName()); + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + + return true; } public Map<String, AuthenticationProviderInitialiser> getMechanisms() @@ -157,21 +293,14 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase public List<Principal> getUsers() { - return new LinkedList<Principal>(); //todo + return new LinkedList<Principal>(_users.values()); } public Principal getUser(String username) { - try - { - if (lookupPassword(username) != null) - { - return new UsernamePrincipal(username); - } - } - catch (IOException e) + if (_users.containsKey(username)) { - //fall through to null return + return new UsernamePrincipal(username); } return null; } @@ -197,49 +326,166 @@ public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase * 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 name of the principal to lookup - * - * @return char[] of the password + * @param name The principal name to lookup * - * @throws java.io.IOException whilst accessing the file + * @return a char[] for use in SASL. */ - private char[] lookupPassword(String name) throws IOException + private char[] lookupPassword(String name) + { + PlainUser user = _users.get(name); + if (user == null) + { + return null; + } + else + { + return user.getPassword(); + } + } + + private void loadPasswordFile() throws IOException + { + try + { + _userUpdate.lock(); + _users.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; + } + + PlainUser user = new PlainUser(result); + _logger.info("Created user:" + user); + _users.put(user.getName(), user); + } + } + finally + { + if (reader != null) + { + reader.close(); + } + } + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + + private void savePasswordFile() throws IOException { - BufferedReader reader = null; try { - reader = new BufferedReader(new FileReader(_passwordFile)); - String line; + _userUpdate.lock(); + + BufferedReader reader = null; + PrintStream writer = null; + File tmp = File.createTempFile(_passwordFile.getName(), ".tmp"); - while ((line = reader.readLine()) != null) + try { - if (!line.startsWith("#")) + 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) + if (result == null || result.length < 2 || result[0].startsWith("#")) { + writer.write(line.getBytes(DEFAULT_ENCODING)); + writer.println(); continue; } - if (name.equals(result[0])) + PlainUser user = _users.get(result[0]); + + if (user == null) { - return result[1].toCharArray(); + 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[] password = user.getPasswordBytes(); + + writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING)); + writer.write(password); + writer.println(); + + user.saved(); + } + } + } + + for (PlainUser user : _users.values()) + { + if (user.isModified()) + { + byte[] password; + password = user.getPasswordBytes(); + writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING)); + writer.write(password); + writer.println(); + user.saved(); } } } - return null; + finally + { + if (reader != null) + { + reader.close(); + } + + if (writer != null) + { + writer.close(); + } + + // Swap temp file to main password file. + File old = new File(_passwordFile.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + _passwordFile.renameTo(old); + tmp.renameTo(_passwordFile); + tmp.delete(); + } } finally { - if (reader != null) + if (_userUpdate.isHeldByCurrentThread()) { - reader.close(); + _userUpdate.unlock(); } } } - + public void reload() throws IOException { - //This PD is not cached, so do nothing. + loadPasswordFile(); } } |