001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.core; 018 019import static org.opends.messages.ConfigMessages.*; 020 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024import java.util.concurrent.ConcurrentHashMap; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.forgerock.opendj.config.server.ConfigException; 028import org.forgerock.opendj.ldap.ResultCode; 029import org.forgerock.opendj.config.server.ConfigurationAddListener; 030import org.forgerock.opendj.config.server.ConfigurationChangeListener; 031import org.forgerock.opendj.config.server.ConfigurationDeleteListener; 032import org.forgerock.opendj.server.config.server.RootDNCfg; 033import org.forgerock.opendj.server.config.server.RootDNUserCfg; 034import org.forgerock.opendj.config.server.ConfigChangeResult; 035import org.forgerock.opendj.ldap.DN; 036import org.opends.server.types.DirectoryException; 037import org.opends.server.types.InitializationException; 038import org.opends.server.types.Privilege; 039 040/** 041 * This class defines a utility that will be used to manage the set of root 042 * users defined in the Directory Server. It will handle both the 043 * "cn=Root DNs,cn=config" entry itself (through the root privilege change 044 * listener), and all of its children. 045 */ 046public class RootDNConfigManager 047 implements ConfigurationChangeListener<RootDNUserCfg>, 048 ConfigurationAddListener<RootDNUserCfg>, 049 ConfigurationDeleteListener<RootDNUserCfg> 050{ 051 /** A mapping between the actual root DNs and their alternate bind DNs. */ 052 private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs; 053 054 /** 055 * The root privilege change listener that will handle changes to the 056 * "cn=Root DNs,cn=config" entry itself. 057 */ 058 private RootPrivilegeChangeListener rootPrivilegeChangeListener; 059 060 private final ServerContext serverContext; 061 062 /** 063 * Creates a new instance of this root DN config manager. 064 * 065 * @param serverContext 066 * The server context. 067 */ 068 public RootDNConfigManager(ServerContext serverContext) 069 { 070 this.serverContext = serverContext; 071 alternateBindDNs = new ConcurrentHashMap<>(); 072 rootPrivilegeChangeListener = new RootPrivilegeChangeListener(); 073 } 074 075 /** 076 * Initializes all of the root users currently defined in the Directory Server 077 * configuration, as well as the set of privileges that root users will 078 * inherit by default. 079 * 080 * @throws ConfigException 081 * If a configuration problem causes the identity mapper 082 * initialization process to fail. 083 * @throws InitializationException 084 * If a problem occurs while initializing the identity mappers that 085 * is not related to the server configuration. 086 */ 087 public void initializeRootDNs() 088 throws ConfigException, InitializationException 089 { 090 RootDNCfg rootDNCfg = serverContext.getRootConfig().getRootDN(); 091 rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg); 092 rootDNCfg.addChangeListener(rootPrivilegeChangeListener); 093 094 rootDNCfg.addRootDNUserAddListener(this); 095 rootDNCfg.addRootDNUserDeleteListener(this); 096 097 // Get the set of root users defined below "cn=Root DNs,cn=config". For 098 // each one, register as a change listener, and get the set of alternate 099 // bind DNs. 100 for (String name : rootDNCfg.listRootDNUsers()) 101 { 102 RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name); 103 rootUserCfg.addChangeListener(this); 104 DirectoryServer.registerRootDN(rootUserCfg.dn()); 105 106 HashSet<DN> altBindDNs = new HashSet<>(); 107 for (DN alternateBindDN : rootUserCfg.getAlternateBindDN()) 108 { 109 try 110 { 111 altBindDNs.add(alternateBindDN); 112 DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(), 113 alternateBindDN); 114 } 115 catch (DirectoryException de) 116 { 117 throw new InitializationException(de.getMessageObject()); 118 } 119 } 120 121 alternateBindDNs.put(rootUserCfg.dn(), altBindDNs); 122 } 123 } 124 125 /** 126 * Retrieves the set of privileges that will be granted to root users by 127 * default. 128 * 129 * @return The set of privileges that will be granted to root users by 130 * default. 131 */ 132 public Set<Privilege> getRootPrivileges() 133 { 134 return rootPrivilegeChangeListener.getDefaultRootPrivileges(); 135 } 136 137 @Override 138 public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration, 139 List<LocalizableMessage> unacceptableReasons) 140 { 141 // The new root user must not have an alternate bind DN that is already 142 // in use. 143 boolean configAcceptable = true; 144 for (DN altBindDN : configuration.getAlternateBindDN()) 145 { 146 DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); 147 if (existingRootDN != null) 148 { 149 unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( 150 altBindDN, configuration.dn(), existingRootDN)); 151 configAcceptable = false; 152 } 153 } 154 155 return configAcceptable; 156 } 157 158 @Override 159 public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration) 160 { 161 configuration.addChangeListener(this); 162 163 final ConfigChangeResult ccr = new ConfigChangeResult(); 164 165 HashSet<DN> altBindDNs = new HashSet<>(); 166 for (DN altBindDN : configuration.getAlternateBindDN()) 167 { 168 try 169 { 170 DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN); 171 altBindDNs.add(altBindDN); 172 } 173 catch (DirectoryException de) 174 { 175 // This shouldn't happen, since the set of DNs should have already been 176 // validated. 177 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 178 ccr.addMessage(de.getMessageObject()); 179 180 for (DN dn : altBindDNs) 181 { 182 DirectoryServer.deregisterAlternateRootBindDN(dn); 183 } 184 break; 185 } 186 } 187 188 if (ccr.getResultCode() == ResultCode.SUCCESS) 189 { 190 DirectoryServer.registerRootDN(configuration.dn()); 191 alternateBindDNs.put(configuration.dn(), altBindDNs); 192 } 193 194 return ccr; 195 } 196 197 @Override 198 public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration, 199 List<LocalizableMessage> unacceptableReasons) 200 { 201 return true; 202 } 203 204 @Override 205 public ConfigChangeResult applyConfigurationDelete( 206 RootDNUserCfg configuration) 207 { 208 DirectoryServer.deregisterRootDN(configuration.dn()); 209 configuration.removeChangeListener(this); 210 211 final ConfigChangeResult ccr = new ConfigChangeResult(); 212 213 HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn()); 214 if (altBindDNs != null) 215 { 216 for (DN dn : altBindDNs) 217 { 218 DirectoryServer.deregisterAlternateRootBindDN(dn); 219 } 220 } 221 222 return ccr; 223 } 224 225 @Override 226 public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration, 227 List<LocalizableMessage> unacceptableReasons) 228 { 229 boolean configAcceptable = true; 230 231 // There must not be any new alternate bind DNs that are already in use by 232 // other root users. 233 for (DN altBindDN: configuration.getAlternateBindDN()) 234 { 235 DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); 236 if (existingRootDN != null && !existingRootDN.equals(configuration.dn())) 237 { 238 unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( 239 altBindDN, configuration.dn(), existingRootDN)); 240 configAcceptable = false; 241 } 242 } 243 244 return configAcceptable; 245 } 246 247 @Override 248 public ConfigChangeResult applyConfigurationChange( 249 RootDNUserCfg configuration) 250 { 251 final ConfigChangeResult ccr = new ConfigChangeResult(); 252 253 HashSet<DN> setDNs = new HashSet<>(); 254 HashSet<DN> addDNs = new HashSet<>(); 255 HashSet<DN> delDNs = new HashSet<>(alternateBindDNs.get(configuration.dn())); 256 257 for (DN altBindDN : configuration.getAlternateBindDN()) 258 { 259 setDNs.add(altBindDN); 260 261 if (! delDNs.remove(altBindDN)) 262 { 263 addDNs.add(altBindDN); 264 } 265 } 266 267 for (DN dn : delDNs) 268 { 269 DirectoryServer.deregisterAlternateRootBindDN(dn); 270 } 271 272 HashSet<DN> addedDNs = new HashSet<>(addDNs.size()); 273 for (DN dn : addDNs) 274 { 275 try 276 { 277 DirectoryServer.registerAlternateRootDN(configuration.dn(), dn); 278 addedDNs.add(dn); 279 } 280 catch (DirectoryException de) 281 { 282 // This shouldn't happen, since the set of DNs should have already been 283 // validated. 284 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 285 ccr.addMessage(de.getMessageObject()); 286 287 for (DN addedDN : addedDNs) 288 { 289 DirectoryServer.deregisterAlternateRootBindDN(addedDN); 290 } 291 292 for (DN deletedDN : delDNs) 293 { 294 try 295 { 296 DirectoryServer.registerAlternateRootDN(configuration.dn(), 297 deletedDN); 298 } 299 catch (Exception e) 300 { 301 // This should also never happen. 302 alternateBindDNs.get(configuration.dn()).remove(deletedDN); 303 } 304 } 305 } 306 } 307 308 if (ccr.getResultCode() == ResultCode.SUCCESS) 309 { 310 alternateBindDNs.put(configuration.dn(), setDNs); 311 } 312 313 return ccr; 314 } 315}