diff options
Diffstat (limited to 'qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java')
-rw-r--r-- | qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java | 407 |
1 files changed, 379 insertions, 28 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java index 208130379e..ee4336055b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java @@ -20,9 +20,19 @@ */ package org.apache.qpid.server.security.auth.management; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; import java.security.Principal; +import java.util.Enumeration; import java.util.List; +import java.util.Properties; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import javax.management.JMException; import javax.management.openmbean.CompositeData; @@ -34,13 +44,17 @@ import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; import javax.security.auth.login.AccountNotFoundException; +import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.management.common.mbeans.UserManagement; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; @@ -51,18 +65,22 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class); private PrincipalDatabase _principalDatabase; + private Properties _accessRights; + private File _accessFile; + + private ReentrantLock _accessRightsUpdate = new ReentrantLock(); // Setup for the TabularType - private static final TabularType _userlistDataType; // Datatype for representing User Lists - private static final CompositeType _userDataType; // Composite type for representing User + static TabularType _userlistDataType; // Datatype for representing User Lists + static CompositeType _userDataType; // Composite type for representing User static { OpenType[] userItemTypes = new OpenType[4]; // User item types. userItemTypes[0] = SimpleType.STRING; // For Username - userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read - No longer in use - userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write - No longer in use - userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin - No longer is use + userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read + userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write + userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin try { @@ -74,11 +92,12 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana } catch (OpenDataException e) { - _logger.error("Tabular data setup for viewing users incorrect.", e); - throw new ExceptionInInitializerError("Tabular data setup for viewing users incorrect"); + _logger.error("Tabular data setup for viewing users incorrect."); + _userlistDataType = null; } } + public AMQUserManagementMBean() throws JMException { super(UserManagement.class, UserManagement.TYPE); @@ -91,23 +110,121 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana public boolean setPassword(String username, String password) { + return setPassword(username, password.toCharArray()); + } + + public boolean setPassword(String username, char[] password) + { try { //delegate password changes to the Principal Database - return _principalDatabase.updatePassword(new UsernamePrincipal(username), password.toCharArray()); + return _principalDatabase.updatePassword(new UsernamePrincipal(username), password); } catch (AccountNotFoundException e) { - _logger.warn("Attempt to set password of non-existent user'" + username + "'"); + _logger.warn("Attempt to set password of non-existant user'" + username + "'"); return false; } } - public boolean createUser(String username, String password) + public boolean setRights(String username, boolean read, boolean write, boolean admin) { - if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password.toCharArray())) + + Object oldRights = null; + if ((oldRights =_accessRights.get(username)) == null) { - return true; + // If the user doesn't exist in the access rights file check that they at least have an account. + if (_principalDatabase.getUser(username) == null) + { + return false; + } + } + + try + { + _accessRightsUpdate.lock(); + + // Update the access rights + if (admin) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.ADMIN); + } + else + { + if (read | write) + { + if (read) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.READONLY); + } + if (write) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.READWRITE); + } + } + else + { + _accessRights.remove(username); + } + } + + //save the rights file + try + { + saveAccessFile(); + } + catch (IOException e) + { + _logger.warn("Problem occured saving '" + _accessFile + "', the access right changes will not be preserved: " + e); + + //the rights file was not successfully saved, restore user rights to previous value + _logger.warn("Reverting attempted rights update for user'" + username + "'"); + if (oldRights != null) + { + _accessRights.put(username, oldRights); + } + else + { + _accessRights.remove(username); + } + + return false; + } + } + finally + { + _accessRightsUpdate.unlock(); + } + + return true; + } + + public boolean createUser(String username, String password, boolean read, boolean write, boolean admin) + { + return createUser(username, password.toCharArray(), read, write, admin); + } + + public boolean createUser(String username, char[] password, boolean read, boolean write, boolean admin) + { + if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password)) + { + if (!setRights(username, read, write, admin)) + { + //unable to set rights for user, remove account + try + { + _principalDatabase.deletePrincipal(new UsernamePrincipal(username)); + } + catch (AccountNotFoundException e) + { + //ignore + } + return false; + } + else + { + return true; + } } return false; @@ -117,7 +234,29 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana { try { - _principalDatabase.deletePrincipal(new UsernamePrincipal(username)); + if (_principalDatabase.deletePrincipal(new UsernamePrincipal(username))) + { + try + { + _accessRightsUpdate.lock(); + + _accessRights.remove(username); + + try + { + saveAccessFile(); + } + catch (IOException e) + { + _logger.warn("Problem occured saving '" + _accessFile + "', the access right changes will not be preserved: " + e); + return false; + } + } + finally + { + _accessRightsUpdate.unlock(); + } + } } catch (AccountNotFoundException e) { @@ -130,23 +269,38 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana public boolean reloadData() { - try - { - _principalDatabase.reload(); - } - catch (IOException e) - { - _logger.warn("Reload failed due to:", e); - return false; - } - // Reload successful - return true; + try + { + loadAccessFile(); + _principalDatabase.reload(); + } + catch (ConfigurationException e) + { + _logger.warn("Reload failed due to:" + e); + return false; + } + catch (IOException e) + { + _logger.warn("Reload failed due to:" + e); + return false; + } + // Reload successful + return true; } - @MBeanOperation(name = "viewUsers", description = "All users that are currently available to the system.") + @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.") public TabularData viewUsers() { + // Table of users + // Username(string), Access rights Read,Write,Admin(bool,bool,bool) + + if (_userlistDataType == null) + { + _logger.warn("TabluarData not setup correctly"); + return null; + } + List<Principal> users = _principalDatabase.getUsers(); TabularDataSupport userList = new TabularDataSupport(_userlistDataType); @@ -157,15 +311,29 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana for (Principal user : users) { // Create header attributes list - // Read,Write,Admin items are depcreated and we return always false. - Object[] itemData = {user.getName(), false, false, false}; + + String rights = (String) _accessRights.get(user.getName()); + + Boolean read = false; + Boolean write = false; + Boolean admin = false; + + if (rights != null) + { + read = rights.equals(MBeanInvocationHandlerImpl.READONLY) + || rights.equals(MBeanInvocationHandlerImpl.READWRITE); + write = rights.equals(MBeanInvocationHandlerImpl.READWRITE); + admin = rights.equals(MBeanInvocationHandlerImpl.ADMIN); + } + + Object[] itemData = {user.getName(), read, write, admin}; CompositeData messageData = new CompositeDataSupport(_userDataType, COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); userList.put(messageData); } } catch (OpenDataException e) { - _logger.warn("Unable to create user list due to :", e); + _logger.warn("Unable to create user list due to :" + e); return null; } @@ -183,4 +351,187 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana { _principalDatabase = database; } + + /** + * setAccessFile + * + * @param accessFile the file to use for updating. + * + * @throws java.io.IOException If the file cannot be accessed + * @throws org.apache.commons.configuration.ConfigurationException + * if checks on the file fail. + */ + public void setAccessFile(String accessFile) throws IOException, ConfigurationException + { + if (accessFile != null) + { + _accessFile = new File(accessFile); + if (!_accessFile.exists()) + { + throw new ConfigurationException("'" + _accessFile + "' does not exist"); + } + + if (!_accessFile.canRead()) + { + throw new ConfigurationException("Cannot read '" + _accessFile + "'."); + } + + if (!_accessFile.canWrite()) + { + _logger.warn("Unable to write to access rights file '" + _accessFile + "', changes will not be preserved."); + } + + loadAccessFile(); + } + else + { + _logger.warn("Access rights file specified is null. Access rights not changed."); + } + } + + private void loadAccessFile() throws IOException, ConfigurationException + { + if(_accessFile == null) + { + _logger.error("No jmx access rights file has been specified."); + return; + } + + if(_accessFile.exists()) + { + try + { + _accessRightsUpdate.lock(); + + Properties accessRights = new Properties(); + FileInputStream inStream = new FileInputStream(_accessFile); + try + { + accessRights.load(inStream); + } + finally + { + inStream.close(); + } + + checkAccessRights(accessRights); + setAccessRights(accessRights); + } + finally + { + _accessRightsUpdate.unlock(); + } + } + else + { + _logger.error("Specified jmxaccess rights file '" + _accessFile + "' does not exist."); + } + } + + private void checkAccessRights(Properties accessRights) + { + Enumeration values = accessRights.propertyNames(); + + while (values.hasMoreElements()) + { + String user = (String) values.nextElement(); + + if (_principalDatabase.getUser(user) == null) + { + _logger.warn("Access rights contains user '" + user + "' but there is no authentication data for that user"); + } + } + } + + private void saveAccessFile() throws IOException + { + try + { + _accessRightsUpdate.lock(); + + // Create temporary file + Random r = new Random(); + File tmp; + do + { + tmp = new File(_accessFile.getPath() + r.nextInt() + ".tmp"); + } + while(tmp.exists()); + + tmp.deleteOnExit(); + + FileOutputStream output = new FileOutputStream(tmp); + _accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser()); + output.close(); + + // Swap temp file to main rights file. + File old = new File(_accessFile.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + + if(!_accessFile.renameTo(old)) + { + //unable to rename the existing file to the backup name + _logger.error("Could not backup the existing management rights file"); + throw new IOException("Could not backup the existing management rights file"); + } + + if(!tmp.renameTo(_accessFile)) + { + //failed to rename the new file to the required filename + + if(!old.renameTo(_accessFile)) + { + //unable to return the backup to required filename + _logger.error("Could not rename the new management rights file into place, and unable to restore original file"); + throw new IOException("Could not rename the new management rights file into place, and unable to restore original file"); + } + + _logger.error("Could not rename the new management rights file into place"); + throw new IOException("Could not rename the new management rights file into place"); + } + } + finally + { + _accessRightsUpdate.unlock(); + } + + } + + private String getCurrentJMXUser() + { + AccessControlContext acc = AccessController.getContext(); + + Subject subject = Subject.getSubject(acc); + if (subject == null) + { + return "Unknown user, authentication Subject was null"; + } + + // Retrieve JMXPrincipal from Subject + Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); + if (principals == null || principals.isEmpty()) + { + return "Unknown user principals were null"; + } + + Principal principal = principals.iterator().next(); + return principal.getName(); + } + + /** + * user=read user=write user=readwrite user=admin + * + * @param accessRights The properties list of access rights to process + */ + private void setAccessRights(Properties accessRights) + { + _logger.debug("Setting Access Rights:" + accessRights); + _accessRights = accessRights; + + // TODO check where this is used + // MBeanInvocationHandlerImpl.setAccessRights(_accessRights); + } } |