| | |
| | | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | import org.opends.server.api.ConfigAddListener; |
| | | import org.opends.server.api.ConfigChangeListener; |
| | | import org.opends.server.api.ConfigDeleteListener; |
| | | import org.opends.server.api.ConfigHandler; |
| | | import org.opends.server.api.ConfigurableComponent; |
| | | import org.opends.server.config.ConfigAttribute; |
| | | import org.opends.server.config.ConfigEntry; |
| | | import org.opends.server.admin.server.ConfigurationAddListener; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.server.ConfigurationDeleteListener; |
| | | import org.opends.server.admin.std.server.RootCfg; |
| | | import org.opends.server.admin.std.server.RootDNCfg; |
| | | import org.opends.server.admin.std.server.RootDNUserCfg; |
| | | import org.opends.server.admin.server.ServerManagementContext; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.config.DNConfigAttribute; |
| | | import org.opends.server.config.MultiChoiceConfigAttribute; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.ConfigMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines a utility that will be used to manage the set of root DNs |
| | | * defined in the Directory Server. The root DN accounts will exist below |
| | | * "cn=Root DNs,cn=config" and must have the ds-cfg-root-dn objectclass. They |
| | | * may optionally have a ds-cfg-bind-dn that may be used to provide an alternate |
| | | * DN for use when binding to the server. |
| | | * This class defines a utility that will be used to manage the set of root |
| | | * users defined in the Directory Server. It will handle both the |
| | | * "cn=Root DNs,cn=config" entry itself (through the root privilege change |
| | | * listener), and all of its children. |
| | | */ |
| | | public class RootDNConfigManager |
| | | implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener, |
| | | ConfigurableComponent |
| | | implements ConfigurationChangeListener<RootDNUserCfg>, |
| | | ConfigurationAddListener<RootDNUserCfg>, |
| | | ConfigurationDeleteListener<RootDNUserCfg> |
| | | |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | // A mapping between the actual root DNs and their alternate bind DNs. |
| | | private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs; |
| | | |
| | | |
| | | |
| | | |
| | | // A mapping between each root DN user entry and a list of the alternate |
| | | // bind DNs for that user. |
| | | private ConcurrentHashMap<DN,List<DN>> bindMappings; |
| | | |
| | | // The DN of the entry that serves as the base for the root DN |
| | | // configuration entries. |
| | | private DN rootDNConfigBaseDN; |
| | | |
| | | // The set of privileges that will be automatically inherited by root users. |
| | | private LinkedHashSet<Privilege> rootPrivileges; |
| | | // The root privilege change listener that will handle changes to the |
| | | // "cn=Root DNs,cn=config" entry itself. |
| | | private RootPrivilegeChangeListener rootPrivilegeChangeListener; |
| | | |
| | | |
| | | |
| | |
| | | */ |
| | | public RootDNConfigManager() |
| | | { |
| | | bindMappings = new ConcurrentHashMap<DN,List<DN>>(); |
| | | alternateBindDNs = new ConcurrentHashMap<DN,HashSet<DN>>(); |
| | | rootPrivilegeChangeListener = new RootPrivilegeChangeListener(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Initializes all root DNs currently defined in the Directory Server |
| | | * configuration. This should only be called at Directory Server startup. |
| | | * Initializes all of the root users currently defined in the Directory Server |
| | | * configuration, as well as the set of privileges that root users will |
| | | * inherit by default. |
| | | * |
| | | * @throws ConfigException If a configuration problem causes the root DN |
| | | * initialization process to fail. |
| | | * @throws ConfigException If a configuration problem causes the identity |
| | | * mapper initialization process to fail. |
| | | * |
| | | * @throws InitializationException If a problem occurs while initializing |
| | | * the root DNs that is not related to the |
| | | * server configuration. |
| | | * the identity mappers that is not related |
| | | * to the server configuration. |
| | | */ |
| | | public void initializeRootDNs() |
| | | throws ConfigException, InitializationException |
| | | { |
| | | // First, get the base configuration entry for the root DNs. |
| | | ConfigHandler configHandler = DirectoryServer.getConfigHandler(); |
| | | ConfigEntry baseEntry; |
| | | try |
| | | // Get the root configuration object. |
| | | ServerManagementContext managementContext = |
| | | ServerManagementContext.getInstance(); |
| | | RootCfg rootConfiguration = |
| | | managementContext.getRootConfiguration(); |
| | | |
| | | |
| | | // Get the root DN configuration object, use it to set the default root |
| | | // privileges, and register a change listener for it. |
| | | RootDNCfg rootDNCfg = rootConfiguration.getRootDN(); |
| | | rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg); |
| | | rootDNCfg.addChangeListener(rootPrivilegeChangeListener); |
| | | |
| | | |
| | | // Register as an add and delete listener for new root DN users. |
| | | rootDNCfg.addRootDNUserAddListener(this); |
| | | rootDNCfg.addRootDNUserDeleteListener(this); |
| | | |
| | | |
| | | // Get the set of root users defined below "cn=Root DNs,cn=config". For |
| | | // each one, register as a change listener, and get the set of alternate |
| | | // bind DNs. |
| | | for (String name : rootDNCfg.listRootDNUsers()) |
| | | { |
| | | rootDNConfigBaseDN = DN.decode(DN_ROOT_DN_CONFIG_BASE); |
| | | baseEntry = configHandler.getConfigEntry(rootDNConfigBaseDN); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name); |
| | | rootUserCfg.addChangeListener(this); |
| | | DirectoryServer.registerRootDN(rootUserCfg.dn()); |
| | | |
| | | HashSet<DN> altBindDNs = new HashSet<DN>(); |
| | | for (DN alternateBindDN : rootUserCfg.getAlternateBindDN()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_CONFIG_ROOTDN_CANNOT_GET_BASE; |
| | | String message = getMessage(msgID, String.valueOf(e)); |
| | | throw new ConfigException(msgID, message, e); |
| | | } |
| | | |
| | | if (baseEntry == null) |
| | | { |
| | | int msgID = MSGID_CONFIG_ROOTDN_BASE_DOES_NOT_EXIST; |
| | | String message = getMessage(msgID); |
| | | throw new ConfigException(msgID, message); |
| | | } |
| | | |
| | | |
| | | // Get the set of privileges that root users should have by default. |
| | | rootPrivileges = new LinkedHashSet<Privilege>( |
| | | Privilege.getDefaultRootPrivileges()); |
| | | |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; |
| | | MultiChoiceConfigAttribute rootPrivStub = |
| | | new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | getMessage(msgID), false, true, false, |
| | | Privilege.getPrivilegeNames()); |
| | | try |
| | | { |
| | | MultiChoiceConfigAttribute rootPrivAttr = |
| | | (MultiChoiceConfigAttribute) |
| | | baseEntry.getConfigAttribute(rootPrivStub); |
| | | if (rootPrivAttr != null) |
| | | { |
| | | ArrayList<Privilege> privList = new ArrayList<Privilege>(); |
| | | for (String value : rootPrivAttr.activeValues()) |
| | | try |
| | | { |
| | | String privName = toLowerCase(value); |
| | | Privilege p = Privilege.privilegeForName(privName); |
| | | if (p == null) |
| | | { |
| | | msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; |
| | | String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | String.valueOf(rootDNConfigBaseDN), |
| | | String.valueOf(value)); |
| | | logError(ErrorLogCategory.CONFIGURATION, |
| | | ErrorLogSeverity.SEVERE_WARNING, message, msgID); |
| | | } |
| | | else |
| | | { |
| | | privList.add(p); |
| | | } |
| | | altBindDNs.add(alternateBindDN); |
| | | DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(), |
| | | alternateBindDN); |
| | | } |
| | | |
| | | rootPrivileges = new LinkedHashSet<Privilege>(privList); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | throw new InitializationException(msgID, message, e); |
| | | } |
| | | |
| | | |
| | | // Register with the configuration base entry as an add and delete listener. |
| | | // So that we will be notified of attempts to create or remove root DN |
| | | // entries. Also, register with the server as a configurable component so |
| | | // that we can detect and apply any changes to the root |
| | | baseEntry.registerAddListener(this); |
| | | baseEntry.registerDeleteListener(this); |
| | | DirectoryServer.registerConfigurableComponent(this); |
| | | |
| | | |
| | | // See if the base entry has any children. If not, then we don't need to |
| | | // do anything else. |
| | | if (! baseEntry.hasChildren()) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Iterate through the child entries and process them as root DN entries. |
| | | for (ConfigEntry childEntry : baseEntry.getChildren().values()) |
| | | { |
| | | StringBuilder unacceptableReason = new StringBuilder(); |
| | | if (! configAddIsAcceptable(childEntry, unacceptableReason)) |
| | | { |
| | | logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_CONFIG_ROOTDN_ENTRY_UNACCEPTABLE, |
| | | String.valueOf(childEntry.getDN()), |
| | | unacceptableReason.toString()); |
| | | continue; |
| | | } |
| | | |
| | | try |
| | | { |
| | | ConfigChangeResult result = applyConfigurationAdd(childEntry); |
| | | if (result.getResultCode() != ResultCode.SUCCESS) |
| | | catch (DirectoryException de) |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | |
| | | List<String> resultMessages = result.getMessages(); |
| | | if ((resultMessages == null) || (resultMessages.isEmpty())) |
| | | { |
| | | buffer.append(getMessage(MSGID_CONFIG_UNKNOWN_UNACCEPTABLE_REASON)); |
| | | } |
| | | else |
| | | { |
| | | Iterator<String> iterator = resultMessages.iterator(); |
| | | |
| | | buffer.append(iterator.next()); |
| | | while (iterator.hasNext()) |
| | | { |
| | | buffer.append(EOL); |
| | | buffer.append(iterator.next()); |
| | | } |
| | | } |
| | | |
| | | logError(ErrorLogCategory.CONFIGURATION, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_CONFIG_ROOTDN_CANNOT_CREATE, |
| | | String.valueOf(childEntry.getDN()), buffer.toString()); |
| | | throw new InitializationException(de.getMessageID(), |
| | | de.getErrorMessage(), de); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR, |
| | | MSGID_CONFIG_PWGENERATOR_CANNOT_CREATE_GENERATOR, |
| | | childEntry.getDN().toString(), String.valueOf(e)); |
| | | } |
| | | |
| | | alternateBindDNs.put(rootUserCfg.dn(), altBindDNs); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of privileges that should automatically be granted to |
| | | * root users when they authenticate. |
| | | * Retrieves the set of privileges that will be granted to root users by |
| | | * default. |
| | | * |
| | | * @return The set of privileges that should automatically be granted to root |
| | | * users when they authenticate. |
| | | * @return The set of privileges that will be granted to root users by |
| | | * default. |
| | | */ |
| | | public Set<Privilege> getRootPrivileges() |
| | | { |
| | | return rootPrivileges; |
| | | return rootPrivilegeChangeListener.getDefaultRootPrivileges(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the configuration entry that will result from a proposed |
| | | * modification is acceptable to this change listener. |
| | | * |
| | | * @param configEntry The configuration entry that will result from |
| | | * the requested update. |
| | | * @param unacceptableReason A buffer to which this method can append a |
| | | * human-readable message explaining why the |
| | | * proposed change is not acceptable. |
| | | * |
| | | * @return <CODE>true</CODE> if the proposed entry contains an acceptable |
| | | * configuration, or <CODE>false</CODE> if it does not. |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean configChangeIsAcceptable(ConfigEntry configEntry, |
| | | StringBuilder unacceptableReason) |
| | | public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | // Make sure that the entry has an appropriate objectclass for a root DN. |
| | | if (! configEntry.hasObjectClass(OC_ROOT_DN)) |
| | | // The new root user must not have an alternate bind DN that is already |
| | | // in use. |
| | | boolean configAcceptable = true; |
| | | for (DN altBindDN : configuration.getAlternateBindDN()) |
| | | { |
| | | int msgID = MSGID_CONFIG_ROOTDN_INVALID_OBJECTCLASS; |
| | | String message = getMessage(msgID, configEntry.getDN().toString()); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // See if the entry has any alternate DNs. If so, then make sure they are |
| | | // valid. |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN; |
| | | DNConfigAttribute alternateDNsStub = |
| | | new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID), |
| | | false, true, false); |
| | | try |
| | | { |
| | | DNConfigAttribute alternateDNsAttr = |
| | | (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub); |
| | | |
| | | if (alternateDNsAttr != null) |
| | | DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); |
| | | if (existingRootDN != null) |
| | | { |
| | | // There were alternate DNs provided, so see if there are any duplicate |
| | | // values that are already registered for a different root DN. |
| | | for (DN alternateBindDN : alternateDNsAttr.pendingValues()) |
| | | { |
| | | DN rootDN = DirectoryServer.getActualRootBindDN(alternateBindDN); |
| | | if ((rootDN != null) && (! rootDN.equals(configEntry.getDN()))) |
| | | { |
| | | msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING; |
| | | String message = getMessage(msgID, String.valueOf(alternateBindDN), |
| | | String.valueOf(configEntry.getDN()), |
| | | String.valueOf(rootDN)); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | } |
| | | int msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING; |
| | | String message = getMessage(msgID, String.valueOf(altBindDN), |
| | | String.valueOf(configuration.dn()), |
| | | String.valueOf(existingRootDN)); |
| | | unacceptableReasons.add(message); |
| | | |
| | | configAcceptable = false; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS; |
| | | String message = getMessage(msgID, String.valueOf(configEntry.getDN()), |
| | | getExceptionMessage(e)); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // If we've gotten here then the root DN entry appears to be acceptable. |
| | | return true; |
| | | return configAcceptable; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Attempts to apply a new configuration to this Directory Server component |
| | | * based on the provided changed entry. |
| | | * |
| | | * @param configEntry The configuration entry that containing the updated |
| | | * configuration for this component. |
| | | * |
| | | * @return Information about the result of processing the configuration |
| | | * change. |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) |
| | | public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration) |
| | | { |
| | | DN configEntryDN = configEntry.getDN(); |
| | | configuration.addChangeListener(this); |
| | | |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | |
| | | // Get the set of alternate bind DNs for the entry, if there are any. |
| | | List<DN> alternateBindDNs = null; |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN; |
| | | DNConfigAttribute alternateDNsStub = |
| | | new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID), |
| | | false, true, false); |
| | | try |
| | | HashSet<DN> altBindDNs = new HashSet<DN>(); |
| | | for (DN altBindDN : configuration.getAlternateBindDN()) |
| | | { |
| | | DNConfigAttribute alternateDNsAttr = |
| | | (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub); |
| | | if (alternateDNsAttr != null) |
| | | try |
| | | { |
| | | alternateBindDNs = alternateDNsAttr.activeValues(); |
| | | DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN); |
| | | altBindDNs.add(altBindDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | // This shouldn't happen, since the set of DNs should have already been |
| | | // validated. |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | messages.add(de.getErrorMessage()); |
| | | |
| | | for (DN dn : altBindDNs) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(dn); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS; |
| | | messages.add(getMessage(msgID, getExceptionMessage(e))); |
| | | |
| | | resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; |
| | | } |
| | | |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | List<DN> existingMappings = bindMappings.get(configEntryDN); |
| | | if (existingMappings != null) |
| | | { |
| | | for (DN mappedDN : existingMappings) |
| | | { |
| | | if ((alternateBindDNs == null) || |
| | | (! alternateBindDNs.contains(mappedDN))) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(mappedDN); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (alternateBindDNs == null) |
| | | { |
| | | alternateBindDNs = new ArrayList<DN>(0); |
| | | } |
| | | else |
| | | { |
| | | for (DN alternateBindDN : alternateBindDNs) |
| | | { |
| | | try |
| | | { |
| | | DirectoryServer.registerAlternateRootDN(configEntryDN, |
| | | alternateBindDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_REGISTER_ALTERNATE_BIND_DN; |
| | | messages.add(getMessage(msgID, String.valueOf(alternateBindDN), |
| | | String.valueOf(configEntryDN), |
| | | de.getErrorMessage())); |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | resultCode = ResultCode.CONSTRAINT_VIOLATION; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | bindMappings.put(configEntryDN, alternateBindDNs); |
| | | DirectoryServer.registerRootDN(configuration.dn()); |
| | | alternateBindDNs.put(configuration.dn(), altBindDNs); |
| | | } |
| | | |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the configuration entry that will result from a proposed |
| | | * add is acceptable to this add listener. |
| | | * |
| | | * @param configEntry The configuration entry that will result from |
| | | * the requested add. |
| | | * @param unacceptableReason A buffer to which this method can append a |
| | | * human-readable message explaining why the |
| | | * proposed entry is not acceptable. |
| | | * |
| | | * @return <CODE>true</CODE> if the proposed entry contains an acceptable |
| | | * configuration, or <CODE>false</CODE> if it does not. |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean configAddIsAcceptable(ConfigEntry configEntry, |
| | | StringBuilder unacceptableReason) |
| | | public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | // Make sure that no entry already exists with the specified DN, and that |
| | | // there is no other root user with a conflicting alternate bind DN. |
| | | DN configEntryDN = configEntry.getDN(); |
| | | if (bindMappings.containsKey(configEntryDN) || |
| | | (DirectoryServer.getActualRootBindDN(configEntryDN) != null)) |
| | | { |
| | | int msgID = MSGID_CONFIG_ROOTDN_EXISTS; |
| | | String message = getMessage(msgID, String.valueOf(configEntryDN)); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Make sure that the entry has the root DN objectclass. |
| | | if (! configEntry.hasObjectClass(OC_ROOT_DN)) |
| | | { |
| | | int msgID = MSGID_CONFIG_ROOTDN_INVALID_OBJECTCLASS; |
| | | String message = getMessage(msgID, configEntryDN.toString()); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | // See if the entry has any alternate DNs. If so, then make sure they are |
| | | // valid. |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN; |
| | | DNConfigAttribute alternateDNsStub = |
| | | new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID), |
| | | false, true, false); |
| | | try |
| | | { |
| | | DNConfigAttribute alternateDNsAttr = |
| | | (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub); |
| | | |
| | | if (alternateDNsAttr != null) |
| | | { |
| | | // There were alternate DNs provided, so see if there are any duplicate |
| | | // values that are already registered for a different root DN. |
| | | for (DN alternateBindDN : alternateDNsAttr.pendingValues()) |
| | | { |
| | | DN rootDN = DirectoryServer.getActualRootBindDN(alternateBindDN); |
| | | if ((rootDN != null) && (! rootDN.equals(configEntryDN))) |
| | | { |
| | | msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING; |
| | | String message = getMessage(msgID, String.valueOf(alternateBindDN), |
| | | String.valueOf(configEntryDN), |
| | | String.valueOf(rootDN)); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | unacceptableReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // If we've gotten here then the root DN entry appears to be acceptable. |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Attempts to apply a new configuration based on the provided added entry. |
| | | * |
| | | * @param configEntry The new configuration entry that contains the |
| | | * configuration to apply. |
| | | * |
| | | * @return Information about the result of processing the configuration |
| | | * change. |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) |
| | | public ConfigChangeResult applyConfigurationDelete( |
| | | RootDNUserCfg configuration) |
| | | { |
| | | DN configEntryDN = configEntry.getDN(); |
| | | DirectoryServer.deregisterRootDN(configuration.dn()); |
| | | configuration.removeChangeListener(this); |
| | | |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | |
| | | // Get the set of alternate bind DNs for the entry, if there are any. |
| | | List<DN> alternateBindDNs = null; |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ALTERNATE_BIND_DN; |
| | | DNConfigAttribute alternateDNsStub = |
| | | new DNConfigAttribute(ATTR_ROOTDN_ALTERNATE_BIND_DN, getMessage(msgID), |
| | | false, true, false); |
| | | try |
| | | HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn()); |
| | | if (altBindDNs != null) |
| | | { |
| | | DNConfigAttribute alternateDNsAttr = |
| | | (DNConfigAttribute) configEntry.getConfigAttribute(alternateDNsStub); |
| | | if (alternateDNsAttr != null) |
| | | for (DN dn : altBindDNs) |
| | | { |
| | | alternateBindDNs = alternateDNsAttr.activeValues(); |
| | | DirectoryServer.deregisterAlternateRootBindDN(dn); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_PARSE_ALTERNATE_BIND_DNS; |
| | | messages.add(getMessage(msgID, getExceptionMessage(e))); |
| | | |
| | | resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; |
| | | } |
| | | |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | List<DN> existingMappings = bindMappings.get(configEntryDN); |
| | | if (existingMappings != null) |
| | | { |
| | | for (DN mappedDN : existingMappings) |
| | | { |
| | | if ((alternateBindDNs == null) || |
| | | (! alternateBindDNs.contains(mappedDN))) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(mappedDN); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (alternateBindDNs == null) |
| | | { |
| | | alternateBindDNs = new ArrayList<DN>(0); |
| | | } |
| | | else |
| | | { |
| | | for (DN alternateBindDN : alternateBindDNs) |
| | | { |
| | | try |
| | | { |
| | | DirectoryServer.registerAlternateRootDN(configEntryDN, |
| | | alternateBindDN); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_CANNOT_REGISTER_ALTERNATE_BIND_DN; |
| | | messages.add(getMessage(msgID, String.valueOf(alternateBindDN), |
| | | String.valueOf(configEntryDN))); |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | resultCode = ResultCode.CONSTRAINT_VIOLATION; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | bindMappings.put(configEntryDN, alternateBindDNs); |
| | | DirectoryServer.registerRootDN(configEntryDN); |
| | | configEntry.registerChangeListener(this); |
| | | } |
| | | |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether it is acceptable to remove the provided configuration |
| | | * entry. |
| | | * |
| | | * @param configEntry The configuration entry that will be removed |
| | | * from the configuration. |
| | | * @param unacceptableReason A buffer to which this method can append a |
| | | * human-readable message explaining why the |
| | | * proposed delete is not acceptable. |
| | | * |
| | | * @return <CODE>true</CODE> if the proposed entry may be removed from the |
| | | * configuration, or <CODE>false</CODE> if not. |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean configDeleteIsAcceptable(ConfigEntry configEntry, |
| | | StringBuilder unacceptableReason) |
| | | public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | // A delete should always be acceptable, so just return true. |
| | | return true; |
| | | } |
| | | boolean configAcceptable = true; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Attempts to apply a new configuration based on the provided deleted entry. |
| | | * |
| | | * @param configEntry The new configuration entry that has been deleted. |
| | | * |
| | | * @return Information about the result of processing the configuration |
| | | * change. |
| | | */ |
| | | public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) |
| | | { |
| | | DN configEntryDN = configEntry.getDN(); |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | |
| | | |
| | | // See if the entry is registered as a root DN. If so, then deregister it |
| | | // and any alternate bind DNs that it might have. |
| | | List<DN> alternateBindDNs = bindMappings.remove(configEntryDN); |
| | | if (alternateBindDNs != null) |
| | | // There must not be any new alternate bind DNs that are already in use by |
| | | // other root users. |
| | | for (DN altBindDN: configuration.getAlternateBindDN()) |
| | | { |
| | | for (DN alternateBindDN : alternateBindDNs) |
| | | DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); |
| | | if ((existingRootDN != null) && |
| | | (! existingRootDN.equals(configuration.dn()))) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(alternateBindDN); |
| | | int msgID = MSGID_CONFIG_ROOTDN_CONFLICTING_MAPPING; |
| | | String message = getMessage(msgID, String.valueOf(altBindDN), |
| | | String.valueOf(configuration.dn()), |
| | | String.valueOf(existingRootDN)); |
| | | unacceptableReasons.add(message); |
| | | |
| | | configAcceptable = false; |
| | | } |
| | | } |
| | | |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired); |
| | | return configAcceptable; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the configuration entry with which this |
| | | * component is associated. |
| | | * |
| | | * @return The DN of the configuration entry with which this |
| | | * component is associated. |
| | | * {@inheritDoc} |
| | | */ |
| | | public DN getConfigurableComponentEntryDN() |
| | | { |
| | | return rootDNConfigBaseDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of configuration attributes that are associated |
| | | * with this configurable component. |
| | | * |
| | | * @return The set of configuration attributes that are associated |
| | | * with this configurable component. |
| | | */ |
| | | public List<ConfigAttribute> getConfigurationAttributes() |
| | | { |
| | | LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>(); |
| | | |
| | | LinkedList<String> currentValues = new LinkedList<String>(); |
| | | for (Privilege p : rootPrivileges) |
| | | { |
| | | currentValues.add(p.getName()); |
| | | } |
| | | |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; |
| | | attrList.add(new MultiChoiceConfigAttribute( |
| | | ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID), |
| | | false, true, false, Privilege.getPrivilegeNames(), |
| | | currentValues)); |
| | | |
| | | return attrList; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided configuration entry has an |
| | | * acceptable configuration for this component. If it does not, |
| | | * then detailed information about the problem(s) should be added to |
| | | * the provided list. |
| | | * |
| | | * @param configEntry The configuration entry for which to |
| | | * make the determination. |
| | | * @param unacceptableReasons A list that can be used to hold |
| | | * messages about why the provided |
| | | * entry does not have an acceptable |
| | | * configuration. |
| | | * |
| | | * @return <CODE>true</CODE> if the provided entry has an |
| | | * acceptable configuration for this component, or |
| | | * <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean hasAcceptableConfiguration(ConfigEntry configEntry, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; |
| | | MultiChoiceConfigAttribute rootPrivStub = |
| | | new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | getMessage(msgID), false, true, false, |
| | | Privilege.getPrivilegeNames()); |
| | | try |
| | | { |
| | | MultiChoiceConfigAttribute rootPrivAttr = |
| | | (MultiChoiceConfigAttribute) |
| | | configEntry.getConfigAttribute(rootPrivStub); |
| | | if (rootPrivAttr != null) |
| | | { |
| | | for (String value : rootPrivAttr.activeValues()) |
| | | { |
| | | String privName = toLowerCase(value); |
| | | Privilege p = Privilege.privilegeForName(privName); |
| | | if (p == null) |
| | | { |
| | | msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; |
| | | String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | String.valueOf(rootDNConfigBaseDN), |
| | | String.valueOf(value)); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; |
| | | String message = getMessage(msgID, getExceptionMessage(e)); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then everything looks OK. |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Makes a best-effort attempt to apply the configuration contained |
| | | * in the provided entry. Information about the result of this |
| | | * processing should be added to the provided message list. |
| | | * Information should always be added to this list if a |
| | | * configuration change could not be applied. If detailed results |
| | | * are requested, then information about the changes applied |
| | | * successfully (and optionally about parameters that were not |
| | | * changed) should also be included. |
| | | * |
| | | * @param configEntry The entry containing the new |
| | | * configuration to apply for this |
| | | * component. |
| | | * @param detailedResults Indicates whether detailed information |
| | | * about the processing should be added to |
| | | * the list. |
| | | * |
| | | * @return Information about the result of the configuration |
| | | * update. |
| | | */ |
| | | public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry, |
| | | boolean detailedResults) |
| | | public ConfigChangeResult applyConfigurationChange( |
| | | RootDNUserCfg configuration) |
| | | { |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | HashSet<DN> setDNs = new HashSet<DN>(); |
| | | HashSet<DN> addDNs = new HashSet<DN>(); |
| | | HashSet<DN> delDNs = |
| | | new HashSet<DN>(alternateBindDNs.get(configuration.dn())); |
| | | |
| | | LinkedHashSet<Privilege> newRootPrivileges = |
| | | new LinkedHashSet<Privilege>(Privilege.getDefaultRootPrivileges()); |
| | | |
| | | int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; |
| | | MultiChoiceConfigAttribute rootPrivStub = |
| | | new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | getMessage(msgID), false, true, false, |
| | | Privilege.getPrivilegeNames()); |
| | | try |
| | | for (DN altBindDN : configuration.getAlternateBindDN()) |
| | | { |
| | | MultiChoiceConfigAttribute rootPrivAttr = |
| | | (MultiChoiceConfigAttribute) |
| | | configEntry.getConfigAttribute(rootPrivStub); |
| | | if (rootPrivAttr != null) |
| | | { |
| | | ArrayList<Privilege> privList = new ArrayList<Privilege>(); |
| | | for (String value : rootPrivAttr.activeValues()) |
| | | { |
| | | String privName = toLowerCase(value); |
| | | Privilege p = Privilege.privilegeForName(privName); |
| | | if (p == null) |
| | | { |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; |
| | | } |
| | | setDNs.add(altBindDN); |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; |
| | | messages.add(getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, |
| | | String.valueOf(rootDNConfigBaseDN), |
| | | String.valueOf(value))); |
| | | } |
| | | else |
| | | { |
| | | privList.add(p); |
| | | } |
| | | if (! delDNs.remove(altBindDN)) |
| | | { |
| | | addDNs.add(altBindDN); |
| | | } |
| | | } |
| | | |
| | | for (DN dn : delDNs) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(dn); |
| | | } |
| | | |
| | | HashSet<DN> addedDNs = new HashSet<DN>(addDNs.size()); |
| | | for (DN dn : addDNs) |
| | | { |
| | | try |
| | | { |
| | | DirectoryServer.registerAlternateRootDN(configuration.dn(), dn); |
| | | addedDNs.add(dn); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | // This shouldn't happen, since the set of DNs should have already been |
| | | // validated. |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | messages.add(de.getErrorMessage()); |
| | | |
| | | for (DN addedDN : addedDNs) |
| | | { |
| | | DirectoryServer.deregisterAlternateRootBindDN(addedDN); |
| | | } |
| | | |
| | | newRootPrivileges = new LinkedHashSet<Privilege>(privList); |
| | | for (DN deletedDN : delDNs) |
| | | { |
| | | try |
| | | { |
| | | DirectoryServer.registerAlternateRootDN(configuration.dn(), |
| | | deletedDN); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // This should also never happen. |
| | | alternateBindDNs.get(configuration.dn()).remove(deletedDN); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; |
| | | } |
| | | |
| | | msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; |
| | | messages.add(getMessage(msgID, getExceptionMessage(e))); |
| | | } |
| | | |
| | | |
| | | if (resultCode == ResultCode.SUCCESS) |
| | | { |
| | | rootPrivileges = newRootPrivileges; |
| | | |
| | | if (detailedResults) |
| | | { |
| | | msgID = MSGID_CONFIG_ROOTDN_UPDATED_PRIVILEGES; |
| | | messages.add(getMessage(msgID)); |
| | | } |
| | | alternateBindDNs.put(configuration.dn(), setDNs); |
| | | } |
| | | |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | | } |