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