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