1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.authz;
18
19
20 import org.apache.ldap.common.name.DnParser;
21 import org.apache.ldap.common.name.LdapName;
22 import org.apache.ldap.common.filter.SimpleNode;
23 import org.apache.ldap.common.filter.BranchNode;
24 import org.apache.ldap.server.DirectoryServiceConfiguration;
25 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
26 import org.apache.ldap.server.partition.DirectoryPartitionNexus;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import javax.naming.Name;
31 import javax.naming.NamingException;
32 import javax.naming.NamingEnumeration;
33 import javax.naming.directory.*;
34 import java.util.*;
35
36
37 /***
38 * A cache for tracking static group membership.
39 *
40 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
41 * @version $Rev$
42 */
43 public class GroupCache
44 {
45 /*** the attribute id for an object class: objectClass */
46 private static final String OC_ATTR = "objectClass";
47 /*** the member attribute for a groupOfNames: member */
48 private static final String MEMBER_ATTR = "member";
49 /*** the member attribute for a groupOfUniqueNames: uniqueMember */
50 private static final String UNIQUEMEMBER_ATTR = "uniqueMember";
51 /*** the groupOfNames objectClass: groupOfNames */
52 private static final String GROUPOFNAMES_OC = "groupOfNames";
53 /*** the groupOfUniqueNames objectClass: groupOfUniqueNames */
54 private static final String GROUPOFUNIQUENAMES_OC = "groupOfUniqueNames";
55 /*** the logger for this class */
56 private static final Logger log = LoggerFactory.getLogger( GroupCache.class );
57
58 /*** String key for the DN of a group to a Set (HashSet) for the Strings of member DNs */
59 private final Map groups = new HashMap();
60 /*** a handle on the partition nexus */
61 private final DirectoryPartitionNexus nexus;
62 /*** the env to use for searching */
63 private final Hashtable env;
64 /*** the normalizing Dn parser for member names */
65 private DnParser parser;
66
67
68 /***
69 * Creates a static group cache.
70 *
71 * @param factoryCfg the context factory configuration for the server
72 */
73 public GroupCache( DirectoryServiceConfiguration factoryCfg ) throws NamingException
74 {
75 this.nexus = factoryCfg.getPartitionNexus();
76 this.env = ( Hashtable ) factoryCfg.getEnvironment().clone();
77 this.parser = new DnParser( new ConcreteNameComponentNormalizer(
78 factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() ) );
79 initialize();
80 }
81
82
83 private void initialize() throws NamingException
84 {
85
86
87
88 BranchNode filter = new BranchNode( BranchNode.OR );
89 filter.addNode( new SimpleNode( OC_ATTR, GROUPOFNAMES_OC, SimpleNode.EQUALITY ) );
90 filter.addNode( new SimpleNode( OC_ATTR, GROUPOFUNIQUENAMES_OC, SimpleNode.EQUALITY ) );
91
92 Iterator suffixes = nexus.listSuffixes( true );
93 while ( suffixes.hasNext() )
94 {
95 String suffix = ( String ) suffixes.next();
96 Name baseDn = new LdapName( suffix );
97 SearchControls ctls = new SearchControls();
98 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
99 NamingEnumeration results = nexus.search( baseDn, env, filter, ctls );
100
101 while ( results.hasMore() )
102 {
103 SearchResult result = ( SearchResult ) results.next();
104 String groupDn = result.getName();
105 groupDn = parser.parse( groupDn ).toString();
106 Attribute members = getMemberAttribute( result.getAttributes() );
107
108 if ( members != null )
109 {
110 Set memberSet = new HashSet( members.size() );
111 addMembers( memberSet, members );
112 groups.put( groupDn, memberSet );
113 }
114 else
115 {
116 log.warn( "Found group '" + groupDn + "' without any member or uniqueMember attributes" );
117 }
118 }
119 results.close();
120 }
121 }
122
123
124 /***
125 * Gets the member attribute regardless of whether groupOfNames or
126 * groupOfUniqueNames is used.
127 *
128 * @param entry the entry inspected for member attributes
129 * @return the member attribute
130 */
131 private Attribute getMemberAttribute( Attributes entry )
132 {
133 Attribute oc = entry.get( OC_ATTR );
134
135 if ( oc == null )
136 {
137 if ( entry.get( MEMBER_ATTR ) != null )
138 {
139 return entry.get( MEMBER_ATTR );
140 }
141
142 if ( entry.get( UNIQUEMEMBER_ATTR ) != null )
143 {
144 return entry.get( UNIQUEMEMBER_ATTR );
145 }
146
147 return null;
148 }
149
150 if ( oc.contains( GROUPOFNAMES_OC ) )
151 {
152 return entry.get( MEMBER_ATTR );
153 }
154
155 if ( oc.contains( GROUPOFUNIQUENAMES_OC ) )
156 {
157 return entry.get( UNIQUEMEMBER_ATTR );
158 }
159
160 return null;
161 }
162
163
164 /***
165 * Adds normalized member DNs to the set of normalized member names.
166 *
167 * @param memberSet the set of member Dns (Strings)
168 * @param members the member attribute values being added
169 * @throws NamingException if there are problems accessing the attr values
170 */
171 private void addMembers( Set memberSet, Attribute members ) throws NamingException
172 {
173 for ( int ii = 0; ii < members.size(); ii++ )
174 {
175
176 String memberDn = ( String ) members.get( ii );
177
178 try
179 {
180 memberDn = parser.parse( memberDn ).toString();
181 }
182 catch ( NamingException e )
183 {
184 log.warn( "Malformed member DN in groupOf[Unique]Names entry. Member not added to GroupCache.", e );
185 }
186
187 memberSet.add( memberDn );
188 }
189 }
190
191
192 /***
193 * Removes a set of member names from an existing set.
194 *
195 * @param memberSet the set of normalized member DNs
196 * @param members the set of member values
197 * @throws NamingException if there are problems accessing the attr values
198 */
199 private void removeMembers( Set memberSet, Attribute members ) throws NamingException
200 {
201 for ( int ii = 0; ii < members.size(); ii++ )
202 {
203
204 String memberDn = ( String ) members.get( ii );
205
206 try
207 {
208 memberDn = parser.parse( memberDn ).toString();
209 }
210 catch ( NamingException e )
211 {
212 log.warn( "Malformed member DN in groupOf[Unique]Names entry. Member not removed from GroupCache.", e );
213 }
214
215 memberSet.remove( memberDn );
216 }
217 }
218
219
220 /***
221 * Adds a groups members to the cache. Called by interceptor to account for new
222 * group additions.
223 *
224 * @param upName the user provided name for the group entry
225 * @param normName the normalized name for the group entry
226 * @param entry the group entry's attributes
227 * @throws NamingException if there are problems accessing the attr values
228 */
229 public void groupAdded( String upName, Name normName, Attributes entry ) throws NamingException
230 {
231 Attribute members = getMemberAttribute( entry );
232
233 if ( members == null )
234 {
235 return;
236 }
237
238 Set memberSet = new HashSet( members.size() );
239 addMembers( memberSet, members );
240 groups.put( normName.toString(), memberSet );
241 }
242
243
244 /***
245 * Deletes a group's members from the cache. Called by interceptor to account for
246 * the deletion of groups.
247 *
248 * @param name the normalized DN of the group entry
249 * @param entry the attributes of entry being deleted
250 */
251 public void groupDeleted( Name name, Attributes entry )
252 {
253 Attribute members = getMemberAttribute( entry );
254
255 if ( members == null )
256 {
257 return;
258 }
259
260 groups.remove( name.toString() );
261 }
262
263
264 /***
265 * Utility method to modify a set of member names based on a modify operation
266 * that changes the members of a group.
267 *
268 * @param memberSet the set of members to be altered
269 * @param modOp the type of modify operation being performed
270 * @param members the members being added, removed or replaced
271 * @throws NamingException if there are problems accessing attribute values
272 */
273 private void modify( Set memberSet, int modOp, Attribute members ) throws NamingException
274 {
275
276 switch ( modOp )
277 {
278 case ( DirContext.ADD_ATTRIBUTE ):
279 addMembers( memberSet, members );
280 break;
281 case ( DirContext.REPLACE_ATTRIBUTE ):
282 if ( members.size() > 0 )
283 {
284 memberSet.clear();
285 addMembers( memberSet, members );
286 }
287 break;
288 case ( DirContext.REMOVE_ATTRIBUTE ):
289 removeMembers( memberSet, members );
290 break;
291 default:
292 throw new InternalError( "Undefined modify operation value of " + modOp );
293 }
294 }
295
296
297 /***
298 * Modifies the cache to reflect changes via modify operations to the group entries.
299 * Called by the interceptor to account for modify ops on groups.
300 *
301 * @param name the normalized name of the group entry modified
302 * @param mods the modification operations being performed
303 * @param entry the group entry being modified
304 * @throws NamingException if there are problems accessing attribute values
305 */
306 public void groupModified( Name name, ModificationItem[] mods, Attributes entry ) throws NamingException
307 {
308 Attribute members = null;
309 String memberAttrId = null;
310 Attribute oc = entry.get( OC_ATTR );
311
312 if ( oc.contains( GROUPOFNAMES_OC ) )
313 {
314 members = entry.get( MEMBER_ATTR );
315 memberAttrId = MEMBER_ATTR;
316 }
317
318 if ( oc.contains( GROUPOFUNIQUENAMES_OC ) )
319 {
320 members = entry.get( UNIQUEMEMBER_ATTR );
321 memberAttrId = UNIQUEMEMBER_ATTR;
322 }
323
324 if ( members == null )
325 {
326 return;
327 }
328
329 for ( int ii = 0; ii < mods.length; ii++ )
330 {
331 if ( memberAttrId.equalsIgnoreCase( mods[ii].getAttribute().getID() ) )
332 {
333 Set memberSet = ( Set ) groups.get( name.toString() );
334 if ( memberSet != null )
335 {
336 modify( memberSet, mods[ii].getModificationOp(), members );
337 }
338 break;
339 }
340 }
341 }
342
343
344 /***
345 * Modifies the cache to reflect changes via modify operations to the group entries.
346 * Called by the interceptor to account for modify ops on groups.
347 *
348 * @param name the normalized name of the group entry modified
349 * @param modOp the modify operation being performed
350 * @param mods the modifications being performed
351 * @param entry the entry being modified
352 * @throws NamingException if there are problems accessing attribute values
353 */
354 public void groupModified( Name name, int modOp, Attributes mods, Attributes entry ) throws NamingException
355 {
356 Attribute members = getMemberAttribute( mods );
357
358 if ( members == null )
359 {
360 return;
361 }
362
363 Set memberSet = ( Set ) groups.get( name.toString() );
364 if ( memberSet != null )
365 {
366 modify( memberSet, modOp, members );
367 }
368 }
369
370
371 /***
372 * Gets the set of groups a user is a member of. The groups are returned
373 * as normalized Name objects within the set.
374 *
375 * @param member the member (user) to get the groups for
376 * @return a Set of Name objects representing the groups
377 * @throws NamingException if there are problems accessing attribute values
378 */
379 public Set getGroups( String member ) throws NamingException
380 {
381 try
382 {
383 member = parser.parse( member ).toString();
384 }
385 catch ( NamingException e )
386 {
387 log.warn( "Malformed member DN. Could not find groups for member in GroupCache. Returning empty set for groups!", e );
388 return Collections.EMPTY_SET;
389 }
390
391 Set memberGroups = null;
392
393 Iterator list = groups.keySet().iterator();
394 while ( list.hasNext() )
395 {
396 String group = ( String ) list.next();
397 Set members = ( Set ) groups.get( group );
398
399 if ( members == null )
400 {
401 continue;
402 }
403
404 if ( members.contains( member ) )
405 {
406 if ( memberGroups == null )
407 {
408 memberGroups = new HashSet();
409 }
410
411 memberGroups.add( new LdapName( group ) );
412 }
413 }
414
415 if ( memberGroups == null )
416 {
417 return Collections.EMPTY_SET;
418 }
419
420 return memberGroups;
421 }
422
423
424 public void groupRenamed( Name oldName, Name newName )
425 {
426 groups.put( newName.toString(), groups.remove( oldName.toString() ) );
427 }
428 }