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.Collection;
032 import java.util.LinkedHashSet;
033 import java.util.List;
034
035 import org.opends.server.admin.std.server.IsMemberOfVirtualAttributeCfg;
036 import org.opends.server.api.Group;
037 import org.opends.server.api.VirtualAttributeProvider;
038 import org.opends.server.config.ConfigException;
039 import org.opends.server.core.DirectoryServer;
040 import org.opends.server.core.SearchOperation;
041 import org.opends.server.loggers.debug.DebugTracer;
042 import org.opends.server.types.AttributeType;
043 import org.opends.server.types.AttributeValue;
044 import org.opends.server.types.ByteString;
045 import org.opends.server.types.ConditionResult;
046 import org.opends.server.types.DebugLogLevel;
047 import org.opends.server.types.DirectoryException;
048 import org.opends.server.types.DN;
049 import org.opends.server.types.Entry;
050 import org.opends.server.types.InitializationException;
051 import org.opends.server.types.MemberList;
052 import org.opends.server.types.SearchFilter;
053 import org.opends.server.types.SearchScope;
054 import org.opends.server.types.VirtualAttributeRule;
055
056 import static org.opends.server.loggers.debug.DebugLogger.*;
057 import static org.opends.server.util.ServerConstants.*;
058
059
060
061 /**
062 * This class implements a virtual attribute provider that is meant to serve the
063 * isMemberOf operational attribute. This attribute will be used to provide a
064 * list of all groups in which the specified user is a member.
065 */
066 public class IsMemberOfVirtualAttributeProvider
067 extends VirtualAttributeProvider<IsMemberOfVirtualAttributeCfg>
068 {
069 /**
070 * The tracer object for the debug logger.
071 */
072 private static final DebugTracer TRACER = getTracer();
073
074 /**
075 * Creates a new instance of this entryDN virtual attribute provider.
076 */
077 public IsMemberOfVirtualAttributeProvider()
078 {
079 super();
080
081 // All initialization should be performed in the
082 // initializeVirtualAttributeProvider method.
083 }
084
085
086
087 /**
088 * {@inheritDoc}
089 */
090 @Override()
091 public void initializeVirtualAttributeProvider(
092 IsMemberOfVirtualAttributeCfg configuration)
093 throws ConfigException, InitializationException
094 {
095 // No initialization is required.
096 }
097
098
099
100 /**
101 * {@inheritDoc}
102 */
103 @Override()
104 public boolean isMultiValued()
105 {
106 return true;
107 }
108
109
110
111 /**
112 * {@inheritDoc}
113 */
114 @Override()
115 public LinkedHashSet<AttributeValue> getValues(Entry entry,
116 VirtualAttributeRule rule)
117 {
118 // FIXME -- This probably isn't the most efficient implementation.
119 LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
120 for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
121 {
122 try
123 {
124 if (g.isMember(entry))
125 {
126 values.add(new AttributeValue(rule.getAttributeType(),
127 g.getGroupDN().toString()));
128 }
129 }
130 catch (Exception e)
131 {
132 if (debugEnabled())
133 {
134 TRACER.debugCaught(DebugLogLevel.ERROR, e);
135 }
136 }
137 }
138
139 return values;
140 }
141
142
143
144 /**
145 * {@inheritDoc}
146 */
147 @Override()
148 public boolean hasValue(Entry entry, VirtualAttributeRule rule)
149 {
150 // FIXME -- This probably isn't the most efficient implementation.
151 for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
152 {
153 try
154 {
155 if (g.isMember(entry))
156 {
157 return true;
158 }
159 }
160 catch (Exception e)
161 {
162 if (debugEnabled())
163 {
164 TRACER.debugCaught(DebugLogLevel.ERROR, e);
165 }
166 }
167 }
168
169 return false;
170 }
171
172
173
174 /**
175 * {@inheritDoc}
176 */
177 @Override()
178 public boolean hasValue(Entry entry, VirtualAttributeRule rule,
179 AttributeValue value)
180 {
181 try
182 {
183 DN groupDN = DN.decode(value.getValue());
184 Group g = DirectoryServer.getGroupManager().getGroupInstance(groupDN);
185 if (g == null)
186 {
187 return false;
188 }
189 else
190 {
191 return g.isMember(entry);
192 }
193 }
194 catch (Exception e)
195 {
196 if (debugEnabled())
197 {
198 TRACER.debugCaught(DebugLogLevel.ERROR, e);
199 }
200
201 return false;
202 }
203 }
204
205
206
207 /**
208 * {@inheritDoc}
209 */
210 @Override()
211 public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
212 Collection<AttributeValue> values)
213 {
214 for (AttributeValue value : values)
215 {
216 if (hasValue(entry, rule, value))
217 {
218 return true;
219 }
220 }
221
222 return false;
223 }
224
225
226
227 /**
228 * {@inheritDoc}
229 */
230 @Override()
231 public ConditionResult matchesSubstring(Entry entry,
232 VirtualAttributeRule rule,
233 ByteString subInitial,
234 List<ByteString> subAny,
235 ByteString subFinal)
236 {
237 // DNs cannot be used in substring matching.
238 return ConditionResult.UNDEFINED;
239 }
240
241
242
243 /**
244 * {@inheritDoc}
245 */
246 @Override()
247 public ConditionResult greaterThanOrEqualTo(Entry entry,
248 VirtualAttributeRule rule,
249 AttributeValue value)
250 {
251 // DNs cannot be used in ordering matching.
252 return ConditionResult.UNDEFINED;
253 }
254
255
256
257 /**
258 * {@inheritDoc}
259 */
260 @Override()
261 public ConditionResult lessThanOrEqualTo(Entry entry,
262 VirtualAttributeRule rule,
263 AttributeValue value)
264 {
265 // DNs cannot be used in ordering matching.
266 return ConditionResult.UNDEFINED;
267 }
268
269
270
271 /**
272 * {@inheritDoc}
273 */
274 @Override()
275 public ConditionResult approximatelyEqualTo(Entry entry,
276 VirtualAttributeRule rule,
277 AttributeValue value)
278 {
279 // DNs cannot be used in approximate matching.
280 return ConditionResult.UNDEFINED;
281 }
282
283
284
285 /**
286 * {@inheritDoc}. This virtual attribute will support search operations only
287 * if one of the following is true about the search filter:
288 * <UL>
289 * <LI>It is an equality filter targeting the associated attribute
290 * type.</LI>
291 * <LI>It is an AND filter in which at least one of the components is an
292 * equality filter targeting the associated attribute type.</LI>
293 * </UL>
294 */
295 @Override()
296 public boolean isSearchable(VirtualAttributeRule rule,
297 SearchOperation searchOperation)
298 {
299 return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
300 0);
301 }
302
303
304
305
306 /**
307 * Indicates whether the provided search filter is one that may be used with
308 * this virtual attribute provider, optionally operating in a recursive manner
309 * to make the determination.
310 *
311 * @param attributeType The attribute type used to hold the entryDN value.
312 * @param searchFilter The search filter for which to make the
313 * determination.
314 * @param depth The current recursion depth for this processing.
315 *
316 * @return {@code true} if the provided filter may be used with this virtual
317 * attribute provider, or {@code false} if not.
318 */
319 private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
320 int depth)
321 {
322 switch (filter.getFilterType())
323 {
324 case AND:
325 if (depth >= MAX_NESTED_FILTER_DEPTH)
326 {
327 return false;
328 }
329
330 for (SearchFilter f : filter.getFilterComponents())
331 {
332 if (isSearchable(attributeType, f, depth+1))
333 {
334 return true;
335 }
336 }
337 return false;
338
339 case EQUALITY:
340 return filter.getAttributeType().equals(attributeType);
341
342 default:
343 return false;
344 }
345 }
346
347
348
349 /**
350 * {@inheritDoc}
351 */
352 @Override()
353 public void processSearch(VirtualAttributeRule rule,
354 SearchOperation searchOperation)
355 {
356 SearchFilter filter = searchOperation.getFilter();
357 Group group = extractGroup(rule.getAttributeType(), filter);
358 if (group == null)
359 {
360 return;
361 }
362
363 DN baseDN = searchOperation.getBaseDN();
364 SearchScope scope = searchOperation.getScope();
365 try
366 {
367 MemberList memberList = group.getMembers();
368 while (memberList.hasMoreMembers())
369 {
370 try
371 {
372 Entry e = memberList.nextMemberEntry();
373 if (e.matchesBaseAndScope(baseDN, scope) &&
374 filter.matchesEntry(e))
375 {
376 searchOperation.returnEntry(e, null);
377 }
378 }
379 catch (Exception e)
380 {
381 if (debugEnabled())
382 {
383 TRACER.debugCaught(DebugLogLevel.ERROR, e);
384 }
385 }
386 }
387 }
388 catch (DirectoryException de)
389 {
390 searchOperation.setResponseData(de);
391 }
392 }
393
394
395
396 /**
397 * Extracts the first group DN encountered in the provided filter, operating
398 * recursively as necessary.
399 *
400 * @param attributeType The attribute type holding the entryDN value.
401 * @param filter The search filter to be processed.
402 *
403 * @return The first group encountered in the provided filter, or
404 * {@code null} if there is no match.
405 */
406 private Group extractGroup(AttributeType attributeType, SearchFilter filter)
407 {
408 switch (filter.getFilterType())
409 {
410 case AND:
411 for (SearchFilter f : filter.getFilterComponents())
412 {
413 Group g = extractGroup(attributeType, f);
414 if (g != null)
415 {
416 return g;
417 }
418 }
419 break;
420
421 case EQUALITY:
422 if (filter.getAttributeType().equals(attributeType))
423 {
424 try
425 {
426 DN dn = DN.decode(filter.getAssertionValue().getValue());
427 return DirectoryServer.getGroupManager().getGroupInstance(dn);
428 }
429 catch (Exception e)
430 {
431 if (debugEnabled())
432 {
433 TRACER.debugCaught(DebugLogLevel.ERROR, e);
434 }
435 }
436 }
437 break;
438 }
439
440 return null;
441 }
442 }
443