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; 018import static org.opends.messages.ConfigMessages.*; 019 020import java.util.HashSet; 021import java.util.List; 022import java.util.Set; 023import java.util.concurrent.ConcurrentHashMap; 024 025import org.forgerock.i18n.LocalizableMessage; 026import org.forgerock.opendj.config.server.ConfigException; 027import org.forgerock.opendj.ldap.ResultCode; 028import org.opends.server.admin.server.ConfigurationAddListener; 029import org.opends.server.admin.server.ConfigurationChangeListener; 030import org.opends.server.admin.server.ConfigurationDeleteListener; 031import org.opends.server.admin.server.ServerManagementContext; 032import org.opends.server.admin.std.server.RootCfg; 033import org.opends.server.admin.std.server.RootDNCfg; 034import org.opends.server.admin.std.server.RootDNUserCfg; 035import org.forgerock.opendj.config.server.ConfigChangeResult; 036import org.forgerock.opendj.ldap.DN; 037import org.opends.server.types.DirectoryException; 038import org.opends.server.types.InitializationException; 039import org.opends.server.types.Privilege; 040 041/** 042 * This class defines a utility that will be used to manage the set of root 043 * users defined in the Directory Server. It will handle both the 044 * "cn=Root DNs,cn=config" entry itself (through the root privilege change 045 * listener), and all of its children. 046 */ 047public class RootDNConfigManager 048 implements ConfigurationChangeListener<RootDNUserCfg>, 049 ConfigurationAddListener<RootDNUserCfg>, 050 ConfigurationDeleteListener<RootDNUserCfg> 051 052{ 053 /** A mapping between the actual root DNs and their alternate bind DNs. */ 054 private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs; 055 056 /** 057 * The root privilege change listener that will handle changes to the 058 * "cn=Root DNs,cn=config" entry itself. 059 */ 060 private RootPrivilegeChangeListener rootPrivilegeChangeListener; 061 062 private final ServerContext serverContext; 063 064 /** 065 * Creates a new instance of this root DN config manager. 066 * 067 * @param serverContext 068 * The server context. 069 */ 070 public RootDNConfigManager(ServerContext serverContext) 071 { 072 this.serverContext = serverContext; 073 alternateBindDNs = new ConcurrentHashMap<>(); 074 rootPrivilegeChangeListener = new RootPrivilegeChangeListener(); 075 } 076 077 /** 078 * Initializes all of the root users currently defined in the Directory Server 079 * configuration, as well as the set of privileges that root users will 080 * inherit by default. 081 * 082 * @throws ConfigException 083 * If a configuration problem causes the identity mapper 084 * initialization process to fail. 085 * @throws InitializationException 086 * If a problem occurs while initializing the identity mappers that 087 * is not related to the server configuration. 088 */ 089 public void initializeRootDNs() 090 throws ConfigException, InitializationException 091 { 092 // Get the root configuration object. 093 ServerManagementContext managementContext = 094 ServerManagementContext.getInstance(); 095 RootCfg rootConfiguration = 096 managementContext.getRootConfiguration(); 097 098 099 // Get the root DN configuration object, use it to set the default root 100 // privileges, and register a change listener for it. 101 RootDNCfg rootDNCfg = rootConfiguration.getRootDN(); 102 rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg); 103 rootDNCfg.addChangeListener(rootPrivilegeChangeListener); 104 105 106 // Register as an add and delete listener for new root DN users. 107 rootDNCfg.addRootDNUserAddListener(this); 108 rootDNCfg.addRootDNUserDeleteListener(this); 109 110 111 // Get the set of root users defined below "cn=Root DNs,cn=config". For 112 // each one, register as a change listener, and get the set of alternate 113 // bind DNs. 114 for (String name : rootDNCfg.listRootDNUsers()) 115 { 116 RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name); 117 rootUserCfg.addChangeListener(this); 118 DirectoryServer.registerRootDN(rootUserCfg.dn()); 119 120 HashSet<DN> altBindDNs = new HashSet<>(); 121 for (DN alternateBindDN : rootUserCfg.getAlternateBindDN()) 122 { 123 try 124 { 125 altBindDNs.add(alternateBindDN); 126 DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(), 127 alternateBindDN); 128 } 129 catch (DirectoryException de) 130 { 131 throw new InitializationException(de.getMessageObject()); 132 } 133 } 134 135 alternateBindDNs.put(rootUserCfg.dn(), altBindDNs); 136 } 137 } 138 139 140 141 /** 142 * Retrieves the set of privileges that will be granted to root users by 143 * default. 144 * 145 * @return The set of privileges that will be granted to root users by 146 * default. 147 */ 148 public Set<Privilege> getRootPrivileges() 149 { 150 return rootPrivilegeChangeListener.getDefaultRootPrivileges(); 151 } 152 153 154 155 /** {@inheritDoc} */ 156 @Override 157 public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration, 158 List<LocalizableMessage> unacceptableReasons) 159 { 160 // The new root user must not have an alternate bind DN that is already 161 // in use. 162 boolean configAcceptable = true; 163 for (DN altBindDN : configuration.getAlternateBindDN()) 164 { 165 DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); 166 if (existingRootDN != null) 167 { 168 unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( 169 altBindDN, configuration.dn(), existingRootDN)); 170 configAcceptable = false; 171 } 172 } 173 174 return configAcceptable; 175 } 176 177 178 179 /** {@inheritDoc} */ 180 @Override 181 public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration) 182 { 183 configuration.addChangeListener(this); 184 185 final ConfigChangeResult ccr = new ConfigChangeResult(); 186 187 HashSet<DN> altBindDNs = new HashSet<>(); 188 for (DN altBindDN : configuration.getAlternateBindDN()) 189 { 190 try 191 { 192 DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN); 193 altBindDNs.add(altBindDN); 194 } 195 catch (DirectoryException de) 196 { 197 // This shouldn't happen, since the set of DNs should have already been 198 // validated. 199 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 200 ccr.addMessage(de.getMessageObject()); 201 202 for (DN dn : altBindDNs) 203 { 204 DirectoryServer.deregisterAlternateRootBindDN(dn); 205 } 206 break; 207 } 208 } 209 210 if (ccr.getResultCode() == ResultCode.SUCCESS) 211 { 212 DirectoryServer.registerRootDN(configuration.dn()); 213 alternateBindDNs.put(configuration.dn(), altBindDNs); 214 } 215 216 return ccr; 217 } 218 219 220 221 /** {@inheritDoc} */ 222 @Override 223 public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration, 224 List<LocalizableMessage> unacceptableReasons) 225 { 226 return true; 227 } 228 229 230 231 /** {@inheritDoc} */ 232 @Override 233 public ConfigChangeResult applyConfigurationDelete( 234 RootDNUserCfg configuration) 235 { 236 DirectoryServer.deregisterRootDN(configuration.dn()); 237 configuration.removeChangeListener(this); 238 239 final ConfigChangeResult ccr = new ConfigChangeResult(); 240 241 HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn()); 242 if (altBindDNs != null) 243 { 244 for (DN dn : altBindDNs) 245 { 246 DirectoryServer.deregisterAlternateRootBindDN(dn); 247 } 248 } 249 250 return ccr; 251 } 252 253 254 255 /** {@inheritDoc} */ 256 @Override 257 public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration, 258 List<LocalizableMessage> unacceptableReasons) 259 { 260 boolean configAcceptable = true; 261 262 // There must not be any new alternate bind DNs that are already in use by 263 // other root users. 264 for (DN altBindDN: configuration.getAlternateBindDN()) 265 { 266 DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN); 267 if (existingRootDN != null && !existingRootDN.equals(configuration.dn())) 268 { 269 unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get( 270 altBindDN, configuration.dn(), existingRootDN)); 271 configAcceptable = false; 272 } 273 } 274 275 return configAcceptable; 276 } 277 278 279 280 /** {@inheritDoc} */ 281 @Override 282 public ConfigChangeResult applyConfigurationChange( 283 RootDNUserCfg configuration) 284 { 285 final ConfigChangeResult ccr = new ConfigChangeResult(); 286 287 HashSet<DN> setDNs = new HashSet<>(); 288 HashSet<DN> addDNs = new HashSet<>(); 289 HashSet<DN> delDNs = new HashSet<>(alternateBindDNs.get(configuration.dn())); 290 291 for (DN altBindDN : configuration.getAlternateBindDN()) 292 { 293 setDNs.add(altBindDN); 294 295 if (! delDNs.remove(altBindDN)) 296 { 297 addDNs.add(altBindDN); 298 } 299 } 300 301 for (DN dn : delDNs) 302 { 303 DirectoryServer.deregisterAlternateRootBindDN(dn); 304 } 305 306 HashSet<DN> addedDNs = new HashSet<>(addDNs.size()); 307 for (DN dn : addDNs) 308 { 309 try 310 { 311 DirectoryServer.registerAlternateRootDN(configuration.dn(), dn); 312 addedDNs.add(dn); 313 } 314 catch (DirectoryException de) 315 { 316 // This shouldn't happen, since the set of DNs should have already been 317 // validated. 318 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 319 ccr.addMessage(de.getMessageObject()); 320 321 for (DN addedDN : addedDNs) 322 { 323 DirectoryServer.deregisterAlternateRootBindDN(addedDN); 324 } 325 326 for (DN deletedDN : delDNs) 327 { 328 try 329 { 330 DirectoryServer.registerAlternateRootDN(configuration.dn(), 331 deletedDN); 332 } 333 catch (Exception e) 334 { 335 // This should also never happen. 336 alternateBindDNs.get(configuration.dn()).remove(deletedDN); 337 } 338 } 339 } 340 } 341 342 if (ccr.getResultCode() == ResultCode.SUCCESS) 343 { 344 alternateBindDNs.put(configuration.dn(), setDNs); 345 } 346 347 return ccr; 348 } 349} 350