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 import org.opends.messages.Message; 029 030 031 032 import java.util.Iterator; 033 import java.util.LinkedList; 034 import java.util.Set; 035 036 import org.opends.server.types.DirectoryConfig; 037 import org.opends.server.types.DirectoryException; 038 import org.opends.server.types.DN; 039 import org.opends.server.types.Entry; 040 import org.opends.server.types.MemberList; 041 import org.opends.server.types.MembershipException; 042 import org.opends.server.types.SearchFilter; 043 import org.opends.server.types.SearchScope; 044 045 import static org.opends.server.loggers.debug.DebugLogger.*; 046 import org.opends.server.loggers.debug.DebugTracer; 047 import org.opends.server.types.DebugLogLevel; 048 import static org.opends.messages.ExtensionMessages.*; 049 import static org.opends.server.util.Validator.*; 050 051 052 053 /** 054 * This class provides an implementation of the {@code MemberList} class that 055 * may be used in conjunction when static groups when additional criteria is to 056 * be used to select a subset of the group members. 057 */ 058 public class FilteredStaticGroupMemberList 059 extends MemberList 060 { 061 /** 062 * The tracer object for the debug logger. 063 */ 064 private static final DebugTracer TRACER = getTracer(); 065 066 067 068 069 // The base DN below which all returned members should exist. 070 private DN baseDN; 071 072 // The DN of the static group with which this member list is associated. 073 private DN groupDN; 074 075 // The entry of the next entry that matches the member list criteria. 076 private Entry nextMatchingEntry; 077 078 // The iterator used to traverse the set of member DNs. 079 private Iterator<DN> memberDNIterator; 080 081 // The set of DNs for the users that are members of the associated static 082 // group. 083 private LinkedList<DN> memberDNs; 084 085 // The membership exception that should be thrown the next time a member is 086 // requested. 087 private MembershipException nextMembershipException; 088 089 // The search filter that all returned members should match. 090 private SearchFilter filter; 091 092 // The search scope to apply against the base DN for the member subset. 093 private SearchScope scope; 094 095 096 097 /** 098 * Creates a new filtered static group member list with the provided 099 * information. 100 * 101 * @param groupDN The DN of the static group with which this member list 102 * is associated. 103 * @param memberDNs The set of DNs for the users that are members of the 104 * associated static group. 105 * @param baseDN The base DN below which all returned members should 106 * exist. If this is {@code null}, then all members will 107 * be considered to match the base and scope criteria. 108 * @param scope The search scope to apply against the base DN when 109 * selecting eligible members. 110 * @param filter The search filter which all returned members should 111 * match. If this is {@code null}, then all members will 112 * be considered eligible. 113 */ 114 public FilteredStaticGroupMemberList(DN groupDN, Set<DN> memberDNs, DN baseDN, 115 SearchScope scope, SearchFilter filter) 116 { 117 ensureNotNull(groupDN, memberDNs); 118 119 this.groupDN = groupDN; 120 this.memberDNs = new LinkedList<DN>(memberDNs); 121 memberDNIterator = memberDNs.iterator(); 122 123 this.baseDN = baseDN; 124 this.filter = filter; 125 126 if (scope == null) 127 { 128 this.scope = SearchScope.WHOLE_SUBTREE; 129 } 130 else 131 { 132 this.scope = scope; 133 } 134 135 nextMatchingEntry = null; 136 nextMembershipException = null; 137 nextMemberInternal(); 138 } 139 140 141 142 /** 143 * Attempts to find the next member that matches the associated criteria. 144 * When this method returns, if {@code nextMembershipException} is 145 * non-{@code null}, then that exception should be thrown on the next attempt 146 * to retrieve a member. If {@code nextMatchingEntry} is non-{@code null}, 147 * then that entry should be returned on the next attempt to retrieve a 148 * member. If both are {@code null}, then there are no more members to 149 * return. 150 */ 151 private void nextMemberInternal() 152 { 153 while (memberDNIterator.hasNext()) 154 { 155 DN nextDN = memberDNIterator.next(); 156 157 158 // Check to see if we can eliminate the entry as a possible match purely 159 // based on base DN and scope. 160 if (baseDN != null) 161 { 162 switch (scope) 163 { 164 case BASE_OBJECT: 165 if (! baseDN.equals(nextDN)) 166 { 167 continue; 168 } 169 break; 170 171 case SINGLE_LEVEL: 172 if (! baseDN.equals(nextDN.getParent())) 173 { 174 continue; 175 } 176 break; 177 178 case SUBORDINATE_SUBTREE: 179 if (baseDN.equals(nextDN) || (! baseDN.isAncestorOf(nextDN))) 180 { 181 continue; 182 } 183 break; 184 185 default: 186 if (! baseDN.isAncestorOf(nextDN)) 187 { 188 continue; 189 } 190 break; 191 } 192 } 193 194 195 // Get the entry for the potential member. If we can't, then populate 196 // the next membership exception. 197 try 198 { 199 Entry memberEntry = DirectoryConfig.getEntry(nextDN); 200 if (memberEntry == null) 201 { 202 Message message = ERR_STATICMEMBERS_NO_SUCH_ENTRY.get( 203 String.valueOf(nextDN), String.valueOf(groupDN)); 204 nextMembershipException = 205 new MembershipException(message, true); 206 return; 207 } 208 209 if (filter == null) 210 { 211 nextMatchingEntry = memberEntry; 212 return; 213 } 214 else 215 { 216 if (filter.matchesEntry(memberEntry)) 217 { 218 nextMatchingEntry = memberEntry; 219 return; 220 } 221 else 222 { 223 continue; 224 } 225 } 226 } 227 catch (DirectoryException de) 228 { 229 if (debugEnabled()) 230 { 231 TRACER.debugCaught(DebugLogLevel.ERROR, de); 232 } 233 234 Message message = ERR_STATICMEMBERS_CANNOT_GET_ENTRY. 235 get(String.valueOf(nextDN), String.valueOf(groupDN), 236 String.valueOf(de.getMessageObject())); 237 nextMembershipException = 238 new MembershipException(message, true, de); 239 return; 240 } 241 } 242 243 244 // If we've gotten here, then there are no more members. 245 nextMatchingEntry = null; 246 nextMembershipException = null; 247 } 248 249 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override() 255 public boolean hasMoreMembers() 256 { 257 if (! memberDNIterator.hasNext()) 258 { 259 return false; 260 } 261 262 return ((nextMatchingEntry != null) || (nextMembershipException != null)); 263 } 264 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override() 271 public DN nextMemberDN() 272 throws MembershipException 273 { 274 if (! memberDNIterator.hasNext()) 275 { 276 return null; 277 } 278 279 Entry e = nextMemberEntry(); 280 if (e == null) 281 { 282 return null; 283 } 284 else 285 { 286 return e.getDN(); 287 } 288 } 289 290 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override() 296 public Entry nextMemberEntry() 297 throws MembershipException 298 { 299 if (! memberDNIterator.hasNext()) 300 { 301 return null; 302 } 303 304 if (nextMembershipException == null) 305 { 306 Entry e = nextMatchingEntry; 307 nextMatchingEntry = null; 308 nextMemberInternal(); 309 return e; 310 } 311 else 312 { 313 MembershipException me = nextMembershipException; 314 nextMembershipException = null; 315 nextMemberInternal(); 316 throw me; 317 } 318 } 319 320 321 322 /** 323 * {@inheritDoc} 324 */ 325 @Override() 326 public void close() 327 { 328 // No implementation is required. 329 } 330 } 331