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 2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.extensions;
028
029
030
031 import java.util.Collections;
032 import java.util.Iterator;
033 import java.util.LinkedHashSet;
034 import java.util.List;
035 import java.util.Set;
036
037 import org.opends.messages.Message;
038 import org.opends.server.admin.std.server.DynamicGroupImplementationCfg;
039 import org.opends.server.api.Group;
040 import org.opends.server.config.ConfigException;
041 import org.opends.server.loggers.ErrorLogger;
042 import org.opends.server.loggers.debug.DebugTracer;
043 import org.opends.server.types.Attribute;
044 import org.opends.server.types.AttributeType;
045 import org.opends.server.types.AttributeValue;
046 import org.opends.server.types.DebugLogLevel;
047 import org.opends.server.types.DirectoryConfig;
048 import org.opends.server.types.DirectoryException;
049 import org.opends.server.types.DN;
050 import org.opends.server.types.Entry;
051 import org.opends.server.types.InitializationException;
052 import org.opends.server.types.LDAPURL;
053 import org.opends.server.types.MemberList;
054 import org.opends.server.types.ObjectClass;
055 import org.opends.server.types.SearchFilter;
056 import org.opends.server.types.SearchScope;
057
058 import static org.opends.messages.ExtensionMessages.*;
059 import static org.opends.server.config.ConfigConstants.*;
060 import static org.opends.server.loggers.debug.DebugLogger.*;
061 import static org.opends.server.util.ServerConstants.*;
062 import static org.opends.server.util.Validator.*;
063
064
065
066 /**
067 * This class provides a dynamic group implementation, in which
068 * membership is determined dynamically based on criteria provided
069 * in the form of one or more LDAP URLs. All dynamic groups should
070 * contain the groupOfURLs object class, with the memberURL attribute
071 * specifying the membership criteria.
072 */
073 public class DynamicGroup
074 extends Group<DynamicGroupImplementationCfg>
075 {
076 /**
077 * The tracer object for the debug logger.
078 */
079 private static final DebugTracer TRACER = getTracer();
080
081 // The DN of the entry that holds the definition for this group.
082 private DN groupEntryDN;
083
084 // The set of the LDAP URLs that define the membership criteria.
085 private LinkedHashSet<LDAPURL> memberURLs;
086
087
088
089 /**
090 * Creates a new, uninitialized dynamic group instance. This is intended for
091 * internal use only.
092 */
093 public DynamicGroup()
094 {
095 super();
096
097 // No initialization is required here.
098 }
099
100
101
102 /**
103 * Creates a new dynamic group instance with the provided information.
104 *
105 * @param groupEntryDN The DN of the entry that holds the definition for
106 * this group. It must not be {@code null}.
107 * @param memberURLs The set of LDAP URLs that define the membership
108 * criteria for this group. It must not be
109 * {@code null}.
110 */
111 public DynamicGroup(DN groupEntryDN, LinkedHashSet<LDAPURL> memberURLs)
112 {
113 super();
114
115 ensureNotNull(groupEntryDN, memberURLs);
116
117 this.groupEntryDN = groupEntryDN;
118 this.memberURLs = memberURLs;
119 }
120
121
122
123 /**
124 * {@inheritDoc}
125 */
126 @Override()
127 public void initializeGroupImplementation(
128 DynamicGroupImplementationCfg configuration)
129 throws ConfigException, InitializationException
130 {
131 // No additional initialization is required.
132 }
133
134
135
136
137 /**
138 * {@inheritDoc}
139 */
140 @Override()
141 public DynamicGroup newInstance(Entry groupEntry)
142 throws DirectoryException
143 {
144 ensureNotNull(groupEntry);
145
146
147 // Get the memberURL attribute from the entry, if there is one, and parse
148 // out the LDAP URLs that it contains.
149 LinkedHashSet<LDAPURL> memberURLs = new LinkedHashSet<LDAPURL>();
150 AttributeType memberURLType =
151 DirectoryConfig.getAttributeType(ATTR_MEMBER_URL_LC, true);
152 List<Attribute> attrList = groupEntry.getAttribute(memberURLType);
153 if (attrList != null)
154 {
155 for (Attribute a : attrList)
156 {
157 for (AttributeValue v : a.getValues())
158 {
159 try
160 {
161 memberURLs.add(LDAPURL.decode(v.getStringValue(), true));
162 }
163 catch (DirectoryException de)
164 {
165 if (debugEnabled())
166 {
167 TRACER.debugCaught(DebugLogLevel.ERROR, de);
168 }
169
170 Message message = ERR_DYNAMICGROUP_CANNOT_DECODE_MEMBERURL.
171 get(v.getStringValue(), String.valueOf(groupEntry.getDN()),
172 de.getMessageObject());
173 ErrorLogger.logError(message);
174 }
175 }
176 }
177 }
178
179 return new DynamicGroup(groupEntry.getDN(), memberURLs);
180 }
181
182
183
184 /**
185 * {@inheritDoc}
186 */
187 @Override()
188 public SearchFilter getGroupDefinitionFilter()
189 throws DirectoryException
190 {
191 // FIXME -- This needs to exclude enhanced groups once we have support for
192 // them.
193 return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" +
194 OC_GROUP_OF_URLS + ")");
195 }
196
197
198
199 /**
200 * {@inheritDoc}
201 */
202 @Override()
203 public boolean isGroupDefinition(Entry entry)
204 {
205 ensureNotNull(entry);
206
207 // FIXME -- This needs to exclude enhanced groups once we have support for
208 //them.
209 ObjectClass groupOfURLsClass =
210 DirectoryConfig.getObjectClass(OC_GROUP_OF_URLS_LC, true);
211 return entry.hasObjectClass(groupOfURLsClass);
212 }
213
214
215
216 /**
217 * {@inheritDoc}
218 */
219 @Override()
220 public DN getGroupDN()
221 {
222 return groupEntryDN;
223 }
224
225
226
227 /**
228 * Retrieves the set of member URLs for this dynamic group. The returned set
229 * must not be altered by the caller.
230 *
231 * @return The set of member URLs for this dynamic group.
232 */
233 public Set<LDAPURL> getMemberURLs()
234 {
235 return memberURLs;
236 }
237
238
239
240 /**
241 * {@inheritDoc}
242 */
243 @Override()
244 public boolean supportsNestedGroups()
245 {
246 // Dynamic groups don't support nesting.
247 return false;
248 }
249
250
251
252 /**
253 * {@inheritDoc}
254 */
255 @Override()
256 public List<DN> getNestedGroupDNs()
257 {
258 // Dynamic groups don't support nesting.
259 return Collections.<DN>emptyList();
260 }
261
262
263
264 /**
265 * {@inheritDoc}
266 */
267 @Override()
268 public void addNestedGroup(DN nestedGroupDN)
269 throws UnsupportedOperationException, DirectoryException
270 {
271 // Dynamic groups don't support nesting.
272 Message message = ERR_DYNAMICGROUP_NESTING_NOT_SUPPORTED.get();
273 throw new UnsupportedOperationException(message.toString());
274 }
275
276
277
278 /**
279 * {@inheritDoc}
280 */
281 @Override()
282 public void removeNestedGroup(DN nestedGroupDN)
283 throws UnsupportedOperationException, DirectoryException
284 {
285 // Dynamic groups don't support nesting.
286 Message message = ERR_DYNAMICGROUP_NESTING_NOT_SUPPORTED.get();
287 throw new UnsupportedOperationException(message.toString());
288 }
289
290
291
292 /**
293 * {@inheritDoc}
294 */
295 @Override()
296 public boolean isMember(DN userDN, Set<DN> examinedGroups)
297 throws DirectoryException
298 {
299 if (! examinedGroups.add(getGroupDN()))
300 {
301 return false;
302 }
303
304 Entry entry = DirectoryConfig.getEntry(userDN);
305 if (entry == null)
306 {
307 return false;
308 }
309 else
310 {
311 return isMember(entry);
312 }
313 }
314
315
316
317 /**
318 * {@inheritDoc}
319 */
320 @Override()
321 public boolean isMember(Entry userEntry, Set<DN> examinedGroups)
322 throws DirectoryException
323 {
324 if (! examinedGroups.add(getGroupDN()))
325 {
326 return false;
327 }
328
329 for (LDAPURL memberURL : memberURLs)
330 {
331 if (memberURL.matchesEntry(userEntry))
332 {
333 return true;
334 }
335 }
336
337 return false;
338 }
339
340
341
342 /**
343 * {@inheritDoc}
344 */
345 @Override()
346 public MemberList getMembers()
347 throws DirectoryException
348 {
349 return new DynamicGroupMemberList(groupEntryDN, memberURLs);
350 }
351
352
353
354 /**
355 * {@inheritDoc}
356 */
357 @Override()
358 public MemberList getMembers(DN baseDN, SearchScope scope,
359 SearchFilter filter)
360 throws DirectoryException
361 {
362 if ((baseDN == null) && (filter == null))
363 {
364 return new DynamicGroupMemberList(groupEntryDN, memberURLs);
365 }
366 else
367 {
368 return new DynamicGroupMemberList(groupEntryDN, memberURLs, baseDN, scope,
369 filter);
370 }
371 }
372
373
374
375 /**
376 * {@inheritDoc}
377 */
378 @Override()
379 public boolean mayAlterMemberList()
380 {
381 return false;
382 }
383
384
385
386 /**
387 * {@inheritDoc}
388 */
389 @Override()
390 public void addMember(Entry userEntry)
391 throws UnsupportedOperationException, DirectoryException
392 {
393 // Dynamic groups don't support altering the member list.
394 Message message = ERR_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get();
395 throw new UnsupportedOperationException(message.toString());
396 }
397
398
399
400 /**
401 * {@inheritDoc}
402 */
403 @Override()
404 public void removeMember(DN userDN)
405 throws UnsupportedOperationException, DirectoryException
406 {
407 // Dynamic groups don't support altering the member list.
408 Message message = ERR_DYNAMICGROUP_ALTERING_MEMBERS_NOT_SUPPORTED.get();
409 throw new UnsupportedOperationException(message.toString());
410 }
411
412
413
414 /**
415 * {@inheritDoc}
416 */
417 @Override()
418 public void toString(StringBuilder buffer)
419 {
420 buffer.append("DynamicGroup(dn=");
421 buffer.append(groupEntryDN);
422 buffer.append(",urls={");
423
424 if (! memberURLs.isEmpty())
425 {
426 Iterator<LDAPURL> iterator = memberURLs.iterator();
427 buffer.append("\"");
428 iterator.next().toString(buffer, false);
429
430 while (iterator.hasNext())
431 {
432 buffer.append("\", ");
433 iterator.next().toString(buffer, false);
434 }
435
436 buffer.append("\"");
437 }
438
439 buffer.append("})");
440 }
441 }
442