/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.core; import org.opends.messages.Message; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; 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.types.ConfigChangeResult; import org.opends.server.types.DirectoryException; import org.opends.server.types.DN; import org.opends.server.types.InitializationException; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import static org.opends.messages.ConfigMessages.*; /** * 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 ConfigurationChangeListener<RootDNUserCfg>, ConfigurationAddListener<RootDNUserCfg>, ConfigurationDeleteListener<RootDNUserCfg> { // A mapping between the actual root DNs and their alternate bind DNs. private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs; // The root privilege change listener that will handle changes to the // "cn=Root DNs,cn=config" entry itself. private RootPrivilegeChangeListener rootPrivilegeChangeListener; /** * Creates a new instance of this root DN config manager. */ public RootDNConfigManager() { alternateBindDNs = new ConcurrentHashMap<DN,HashSet<DN>>(); rootPrivilegeChangeListener = new RootPrivilegeChangeListener(); } /** * 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 identity * mapper initialization process to fail. * * @throws InitializationException If a problem occurs while initializing * the identity mappers that is not related * to the server configuration. */ public void initializeRootDNs() throws ConfigException, InitializationException { // 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()) { RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name); rootUserCfg.addChangeListener(this); DirectoryServer.registerRootDN(rootUserCfg.dn()); HashSet<DN> altBindDNs = new HashSet<DN>(); for (DN alternateBindDN : rootUserCfg.getAlternateBindDN()) { try { altBindDNs.add(alternateBindDN); DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(), alternateBindDN); } catch (DirectoryException de) { throw new InitializationException(de.getMessageObject()); } } alternateBindDNs.put(rootUserCfg.dn(), altBindDNs); } } /** * Retrieves the set of privileges that will be granted to root users by * default. * * @return The set of privileges that will be granted to root users by * default. */ public Set<Privilege> getRootPrivileges() { return rootPrivilegeChangeListener.getDefaultRootPrivileges(); } /** * {@inheritDoc} */ public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration, List<Message> unacceptableReasons) { // The new root user must not have an alternate bind DN that is already // in use. boolean configAcceptable = true; for (DN altBindDN : configuration.getAlternateBindDN()) { DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); if (existingRootDN != null) { Message message = ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( String.valueOf(altBindDN), String.valueOf(configuration.dn()), String.valueOf(existingRootDN)); unacceptableReasons.add(message); configAcceptable = false; } } return configAcceptable; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration) { configuration.addChangeListener(this); ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; ArrayList<Message> messages = new ArrayList<Message>(); HashSet<DN> altBindDNs = new HashSet<DN>(); for (DN altBindDN : configuration.getAlternateBindDN()) { try { 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.getMessageObject()); for (DN dn : altBindDNs) { DirectoryServer.deregisterAlternateRootBindDN(dn); } break; } } if (resultCode == ResultCode.SUCCESS) { DirectoryServer.registerRootDN(configuration.dn()); alternateBindDNs.put(configuration.dn(), altBindDNs); } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration, List<Message> unacceptableReasons) { return true; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationDelete( RootDNUserCfg configuration) { DirectoryServer.deregisterRootDN(configuration.dn()); configuration.removeChangeListener(this); ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; ArrayList<Message> messages = new ArrayList<Message>(); HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn()); if (altBindDNs != null) { for (DN dn : altBindDNs) { DirectoryServer.deregisterAlternateRootBindDN(dn); } } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration, List<Message> unacceptableReasons) { boolean configAcceptable = true; // There must not be any new alternate bind DNs that are already in use by // other root users. for (DN altBindDN: configuration.getAlternateBindDN()) { DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); if ((existingRootDN != null) && (! existingRootDN.equals(configuration.dn()))) { Message message = ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( String.valueOf(altBindDN), String.valueOf(configuration.dn()), String.valueOf(existingRootDN)); unacceptableReasons.add(message); configAcceptable = false; } } return configAcceptable; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationChange( RootDNUserCfg configuration) { ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; ArrayList<Message> messages = new ArrayList<Message>(); HashSet<DN> setDNs = new HashSet<DN>(); HashSet<DN> addDNs = new HashSet<DN>(); HashSet<DN> delDNs = new HashSet<DN>(alternateBindDNs.get(configuration.dn())); for (DN altBindDN : configuration.getAlternateBindDN()) { setDNs.add(altBindDN); 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.getMessageObject()); for (DN addedDN : addedDNs) { DirectoryServer.deregisterAlternateRootBindDN(addedDN); } for (DN deletedDN : delDNs) { try { DirectoryServer.registerAlternateRootDN(configuration.dn(), deletedDN); } catch (Exception e) { // This should also never happen. alternateBindDNs.get(configuration.dn()).remove(deletedDN); } } } } if (resultCode == ResultCode.SUCCESS) { alternateBindDNs.put(configuration.dn(), setDNs); } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } }