Package org.apache.qpid.server.security.auth.database

Source Code of org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase$User

/*
*  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.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.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.UnsupportedEncodingException;
import java.io.PrintStream;
import java.util.regex.Pattern;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
import java.security.Principal;
import java.security.NoSuchAlgorithmException;

/**
* Represents a user database where the account information is stored in a simple flat file.
*
* The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
*
* where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
*/
public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase
{
    private static final Logger _logger = Logger.getLogger(Base64MD5PasswordFilePrincipalDatabase.class);

    private File _passwordFile;

    private Pattern _regexp = Pattern.compile(":");

    private Map<String, AuthenticationProviderInitialiser> _saslServers;

    AMQUserManagementMBean _mbean;
    private static final String DEFAULT_ENCODING = "utf-8";
    private Map<String, User> _users = new HashMap<String, User>();
    private ReentrantLock _userUpdate = new ReentrantLock();

    public Base64MD5PasswordFilePrincipalDatabase()
    {
        _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();

        /**
         *  Create Authenticators for MD5 Password file.
         */

        // Accept Plain incomming and hash it for comparison to the file.
        CRAMMD5HashedInitialiser cram = new CRAMMD5HashedInitialiser();
        cram.initialise(this);
        _saslServers.put(cram.getMechanismName(), cram);

        //fixme The PDs should setup a PD Mangement MBean
//        try
//        {
//            _mbean = new AMQUserManagementMBean();
//            _mbean.setPrincipalDatabase(this);
//        }
//        catch (JMException e)
//        {
//            _logger.warn("User management disabled as unable to create MBean:" + e);
//        }
    }

    public void setPasswordFile(String passwordFile) throws IOException
    {
        File f = new File(passwordFile);
        _logger.info("PasswordFilePrincipalDatabase 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
     *
     * @param principal The Principal to set the password for
     * @param callback  The PasswordCallback to call setPassword on
     *
     * @throws AccountNotFoundException If the Principal cannont be found in this Database
     */
    public 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);
        }
    }

    /**
     * Used to verify that the presented Password is correct. Currently only used by Management Console
     *
     * @param principal The principal to authenticate
     * @param password  The 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
    {
        char[] pwd = lookupPassword(principal);

        int index = 0;
        boolean verified = true;

        while (verified & index < password.length)
        {
            verified = (pwd[index] == password[index]);
            index++;
        }
        return verified;
    }

    public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
    {
        User 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 + "' will revert at restart");
                    //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)
    {
        if (_users.get(principal.getName()) != null)
        {
            return false;
        }

        User user = new User(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());
                return false;
            }
        }
        finally
        {
            if (_userUpdate.isHeldByCurrentThread())
            {
                _userUpdate.unlock();
            }
        }
    }

    public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
    {
        User user = _users.get(principal.getName());

        if (user == null)
        {
            throw new AccountNotFoundException(principal.getName());
        }

        try
        {
            _userUpdate.lock();
            user.delete();

            try
            {
                savePasswordFile();
            }
            catch (IOException e)
            {
                _logger.warn("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()
    {
        return _saslServers;
    }

    public List<Principal> getUsers()
    {
        return new LinkedList<Principal>(_users.values());
    }

    public Principal getUser(String username)
    {
        if (_users.containsKey(username))
        {
            return new UsernamePrincipal(username);
        }
        return null;
    }

    /**
     * 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.
     */
    private char[] lookupPassword(String name)
    {
        User 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;
                    }

                    User user = new User(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 = new File(_passwordFile.getAbsolutePath() + ".tmp");
            if (tmp.exists())
            {
                tmp.delete();
            }
            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));
                        continue;
                    }

                    User 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
                        {
                            try
                            {
                                byte[] encodedPassword = user.getEncodePassword();

                                writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
                                writer.write(encodedPassword);
                                writer.println();

                                user.saved();
                            }
                            catch (Exception e)
                            {
                                _logger.warn("Unable to encode new password reverting to old password.");
                                writer.write(line.getBytes(DEFAULT_ENCODING));
                                writer.println();
                            }
                        }
                    }
                }

                for (User user : _users.values())
                {
                    if (user.isModified())
                    {
                        byte[] encodedPassword;
                        try
                        {
                            encodedPassword = user.getEncodePassword();
                            writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING));
                            writer.write(encodedPassword);
                            writer.println();
                            user.saved();
                        }
                        catch (Exception e)
                        {
                            _logger.warn("Unable to get Encoded password for user'" + user.getName() + "' password not saved");
                        }
                    }
                }
            }
            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 (_userUpdate.isHeldByCurrentThread())
            {
                _userUpdate.unlock();
            }
        }
    }

    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;
        }

    }
}
TOP

Related Classes of org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase$User

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.