You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ri...@apache.org on 2009/04/13 15:41:14 UTC

svn commit: r764455 - in /qpid/branches/0.5-fix/qpid: ./ java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ java/broker/src/test/java/org/apache/qpid/server/security/auth/database/

Author: ritchiem
Date: Mon Apr 13 13:41:13 2009
New Revision: 764455

URL: http://svn.apache.org/viewvc?rev=764455&view=rev
Log:
QPID-1502: Update the PlainPasswordFilePrincipalDatabase to be manipulatable by the management console and cached in memory like the B64MD5 PD. Add unit tests for the PlainPD

merged from trunk r748641

Added:
    qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainUser.java
      - copied unchanged from r748641, qpid/trunk/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainUser.java
    qpid/branches/0.5-fix/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java
      - copied unchanged from r748641, qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java
    qpid/branches/0.5-fix/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java
      - copied unchanged from r748641, qpid/trunk/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainUserTest.java
Modified:
    qpid/branches/0.5-fix/qpid/   (props changed)
    qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java

Propchange: qpid/branches/0.5-fix/qpid/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Apr 13 13:41:13 2009
@@ -1 +1 @@
-/qpid/trunk/qpid:742626,743015,743028-743029,743304,743306,743311,743357,744113,747363,747367,747369-747370,747376,747783,747868-747870,747875,748561,748591
+/qpid/trunk/qpid:742626,743015,743028-743029,743304,743306,743311,743357,744113,747363,747367,747369-747370,747376,747783,747868-747870,747875,748561,748591,748641

Modified: qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java
URL: http://svn.apache.org/viewvc/qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java?rev=764455&r1=764454&r2=764455&view=diff
==============================================================================
--- qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java (original)
+++ qpid/branches/0.5-fix/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java Mon Apr 13 13:41:13 2009
@@ -34,11 +34,13 @@
 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 @@
  */
 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 @@
         _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 @@
             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 @@
             throw new IllegalArgumentException("principal must not be null");
         }
         char[] pwd = lookupPassword(principal.getName());
+        
         if (pwd != null)
         {
             callback.setPassword(pwd);
@@ -121,33 +139,151 @@
         }
     }
 
+    /**
+     * 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 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 @@
      * 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
     {
-        BufferedReader reader = null;
         try
         {
-            reader = new BufferedReader(new FileReader(_passwordFile));
-            String line;
+            _userUpdate.lock();
+            _users.clear();
 
-            while ((line = reader.readLine()) != null)
+            BufferedReader reader = null;
+            try
             {
-                if (!line.startsWith("#"))
+                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("#"))
                     {
                         continue;
                     }
 
-                    if (name.equals(result[0]))
+                    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
+    {
+        try
+        {
+            _userUpdate.lock();
+
+            BufferedReader reader = null;
+            PrintStream writer = null;
+            File tmp = File.createTempFile(_passwordFile.getName(), ".tmp");
+
+            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;
+                    }
+
+                    PlainUser user = _users.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[] 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())
                     {
-                        return result[1].toCharArray();
+                        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();
     }
 }



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org