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