1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.authz;
21
22
23 import javax.naming.directory.SearchControls;
24 import javax.naming.NamingException;
25
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.directory.server.constants.ServerDNConstants;
34 import org.apache.directory.server.core.CoreSession;
35 import org.apache.directory.server.core.entry.ServerAttribute;
36 import org.apache.directory.server.core.entry.ServerEntry;
37 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
38 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
39 import org.apache.directory.server.core.partition.PartitionNexus;
40 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
41 import org.apache.directory.server.schema.registries.Registries;
42 import org.apache.directory.shared.ldap.constants.SchemaConstants;
43 import org.apache.directory.shared.ldap.entry.EntryAttribute;
44 import org.apache.directory.shared.ldap.entry.Modification;
45 import org.apache.directory.shared.ldap.entry.ModificationOperation;
46 import org.apache.directory.shared.ldap.entry.Value;
47 import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
48 import org.apache.directory.shared.ldap.filter.BranchNode;
49 import org.apache.directory.shared.ldap.filter.EqualityNode;
50 import org.apache.directory.shared.ldap.filter.OrNode;
51 import org.apache.directory.shared.ldap.message.AliasDerefMode;
52 import org.apache.directory.shared.ldap.name.LdapDN;
53 import org.apache.directory.shared.ldap.schema.AttributeType;
54 import org.apache.directory.shared.ldap.schema.OidNormalizer;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58
59
60
61
62
63
64
65 public class GroupCache
66 {
67
68 private static final Logger LOG = LoggerFactory.getLogger( GroupCache.class );
69
70
71 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
72
73
74 private final Map<String, Set<String>> groups = new HashMap<String, Set<String>>();
75
76
77 private final PartitionNexus nexus;
78
79
80 private AttributeType memberAT;
81
82
83 private AttributeType uniqueMemberAT;
84
85
86
87
88 private Map<String, OidNormalizer> normalizerMap;
89
90
91 private LdapDN administratorsGroupDn;
92
93 private static final Set<LdapDN> EMPTY_GROUPS = new HashSet<LdapDN>();
94
95
96
97
98
99
100
101
102 public GroupCache( CoreSession session ) throws Exception
103 {
104 normalizerMap = session.getDirectoryService().getRegistries().getAttributeTypeRegistry().getNormalizerMapping();
105 nexus = session.getDirectoryService().getPartitionNexus();
106 AttributeTypeRegistry attributeTypeRegistry = session.getDirectoryService()
107 .getRegistries().getAttributeTypeRegistry();
108
109 memberAT = attributeTypeRegistry.lookup( SchemaConstants.MEMBER_AT_OID );
110 uniqueMemberAT = attributeTypeRegistry.lookup( SchemaConstants.UNIQUE_MEMBER_AT_OID );
111
112
113 administratorsGroupDn = parseNormalized( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
114
115 initialize( session );
116 }
117
118
119 private LdapDN parseNormalized( String name ) throws NamingException
120 {
121 LdapDN dn = new LdapDN( name );
122 dn.normalize( normalizerMap );
123 return dn;
124 }
125
126
127 private void initialize( CoreSession session ) throws Exception
128 {
129
130
131
132 BranchNode filter = new OrNode();
133 filter.addNode( new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
134 SchemaConstants.GROUP_OF_NAMES_OC ) ) );
135 filter.addNode( new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, new ClientStringValue(
136 SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ) ) );
137
138 Iterator<String> suffixes = nexus.listSuffixes( null );
139
140 while ( suffixes.hasNext() )
141 {
142 String suffix = suffixes.next();
143 LdapDN baseDn = new LdapDN( suffix );
144 SearchControls ctls = new SearchControls();
145 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
146
147
148 EntryFilteringCursor results = nexus.search( new SearchOperationContext( session,
149 baseDn, AliasDerefMode.DEREF_ALWAYS, filter, ctls ) );
150
151 while ( results.next() )
152 {
153 ServerEntry result = results.get();
154 LdapDN groupDn = result.getDn().normalize( normalizerMap );
155 EntryAttribute members = getMemberAttribute( result );
156
157 if ( members != null )
158 {
159 Set<String> memberSet = new HashSet<String>( members.size() );
160 addMembers( memberSet, members );
161 groups.put( groupDn.getNormName(), memberSet );
162 }
163 else
164 {
165 LOG.warn( "Found group '{}' without any member or uniqueMember attributes", groupDn.getUpName() );
166 }
167 }
168
169 results.close();
170 }
171
172 if ( IS_DEBUG )
173 {
174 LOG.debug( "group cache contents on startup:\n {}", groups );
175 }
176 }
177
178
179
180
181
182
183
184
185
186 private EntryAttribute getMemberAttribute( ServerEntry entry ) throws NamingException
187 {
188 EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
189
190 if ( oc == null )
191 {
192 EntryAttribute member = entry.get( memberAT );
193
194 if ( member != null )
195 {
196 return member;
197 }
198
199 EntryAttribute uniqueMember = entry.get( uniqueMemberAT );
200
201 if ( uniqueMember != null )
202 {
203 return uniqueMember;
204 }
205
206 return null;
207 }
208
209 if ( oc.contains( SchemaConstants.GROUP_OF_NAMES_OC ) || oc.contains( SchemaConstants.GROUP_OF_NAMES_OC_OID ) )
210 {
211 return entry.get( memberAT );
212 }
213
214 if ( oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC )
215 || oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC_OID ) )
216 {
217 return entry.get( uniqueMemberAT );
218 }
219
220 return null;
221 }
222
223
224
225
226
227
228
229
230
231 private void addMembers( Set<String> memberSet, EntryAttribute members ) throws NamingException
232 {
233 for ( Value<?> value : members )
234 {
235
236
237 String memberDn = ( String ) value.get();
238
239 try
240 {
241 memberDn = parseNormalized( memberDn ).toString();
242 }
243 catch ( NamingException e )
244 {
245 LOG.warn( "Malformed member DN in groupOf[Unique]Names entry. Member not added to GroupCache.", e );
246 }
247
248 memberSet.add( memberDn );
249 }
250 }
251
252
253
254
255
256
257
258
259
260 private void removeMembers( Set<String> memberSet, EntryAttribute members ) throws NamingException
261 {
262 for ( Value<?> value : members )
263 {
264
265 String memberDn = ( String ) value.get();
266
267 try
268 {
269 memberDn = parseNormalized( memberDn ).toString();
270 }
271 catch ( NamingException e )
272 {
273 LOG.warn( "Malformed member DN in groupOf[Unique]Names entry. Member not removed from GroupCache.", e );
274 }
275
276 memberSet.remove( memberDn );
277 }
278 }
279
280
281
282
283
284
285
286
287
288
289 public void groupAdded( LdapDN name, ServerEntry entry ) throws NamingException
290 {
291 EntryAttribute members = getMemberAttribute( entry );
292
293 if ( members == null )
294 {
295 return;
296 }
297
298 Set<String> memberSet = new HashSet<String>( members.size() );
299 addMembers( memberSet, members );
300 groups.put( name.getNormName(), memberSet );
301
302 if ( IS_DEBUG )
303 {
304 LOG.debug( "group cache contents after adding '{}' :\n {}", name.getUpName(), groups );
305 }
306 }
307
308
309
310
311
312
313
314
315
316 public void groupDeleted( LdapDN name, ServerEntry entry ) throws NamingException
317 {
318 EntryAttribute members = getMemberAttribute( entry );
319
320 if ( members == null )
321 {
322 return;
323 }
324
325 groups.remove( name.getNormName() );
326
327 if ( IS_DEBUG )
328 {
329 LOG.debug( "group cache contents after deleting '{}' :\n {}", name.getUpName(), groups );
330 }
331 }
332
333
334
335
336
337
338
339
340
341
342
343 private void modify( Set<String> memberSet, ModificationOperation modOp, EntryAttribute members )
344 throws NamingException
345 {
346
347 switch ( modOp )
348 {
349 case ADD_ATTRIBUTE:
350 addMembers( memberSet, members );
351 break;
352
353 case REPLACE_ATTRIBUTE:
354 if ( members.size() > 0 )
355 {
356 memberSet.clear();
357 addMembers( memberSet, members );
358 }
359
360 break;
361
362 case REMOVE_ATTRIBUTE:
363 removeMembers( memberSet, members );
364 break;
365
366 default:
367 throw new InternalError( "Undefined modify operation value of " + modOp );
368 }
369 }
370
371
372
373
374
375
376
377
378
379
380
381 public void groupModified( LdapDN name, List<Modification> mods, ServerEntry entry, Registries registries )
382 throws NamingException
383 {
384 EntryAttribute members = null;
385 String memberAttrId = null;
386 EntryAttribute oc = entry.get( SchemaConstants.OBJECT_CLASS_AT );
387
388 if ( oc.contains( SchemaConstants.GROUP_OF_NAMES_OC ) )
389 {
390 members = entry.get( memberAT );
391 memberAttrId = SchemaConstants.MEMBER_AT;
392 }
393
394 if ( oc.contains( SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC ) )
395 {
396 members = entry.get( uniqueMemberAT );
397 memberAttrId = SchemaConstants.UNIQUE_MEMBER_AT;
398 }
399
400 if ( members == null )
401 {
402 return;
403 }
404
405 for ( Modification modification : mods )
406 {
407 if ( memberAttrId.equalsIgnoreCase( modification.getAttribute().getId() ) )
408 {
409 Set<String> memberSet = groups.get( name.getNormName() );
410
411 if ( memberSet != null )
412 {
413 modify( memberSet, modification.getOperation(), ( ServerAttribute ) modification.getAttribute() );
414 }
415
416 break;
417 }
418 }
419
420 if ( IS_DEBUG )
421 {
422 LOG.debug( "group cache contents after modifying '{}' :\n {}", name.getUpName(), groups );
423 }
424 }
425
426
427
428
429
430
431
432
433
434
435
436 public void groupModified( LdapDN name, ModificationOperation modOp, ServerEntry mods ) throws NamingException
437 {
438 EntryAttribute members = getMemberAttribute( mods );
439
440 if ( members == null )
441 {
442 return;
443 }
444
445 Set<String> memberSet = groups.get( name.getNormName() );
446
447 if ( memberSet != null )
448 {
449 modify( memberSet, modOp, members );
450 }
451
452 if ( IS_DEBUG )
453 {
454 LOG.debug( "group cache contents after modifying '{}' :\n {}", name.getUpName(), groups );
455 }
456 }
457
458
459
460
461
462
463
464
465
466 public final boolean isPrincipalAnAdministrator( LdapDN principalDn )
467 {
468 if ( principalDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ) )
469 {
470 return true;
471 }
472
473 Set<String> members = groups.get( administratorsGroupDn.getNormName() );
474
475 if ( members == null )
476 {
477 LOG.warn( "What do you mean there is no administrators group? This is bad news." );
478 return false;
479 }
480
481 return members.contains( principalDn.toNormName() );
482 }
483
484
485
486
487
488
489
490
491
492
493 public Set<LdapDN> getGroups( String member ) throws NamingException
494 {
495 LdapDN normMember;
496
497 try
498 {
499 normMember = parseNormalized( member );
500 }
501 catch ( NamingException e )
502 {
503 LOG
504 .warn(
505 "Malformed member DN. Could not find groups for member '{}' in GroupCache. Returning empty set for groups!",
506 member, e );
507 return EMPTY_GROUPS;
508 }
509
510 Set<LdapDN> memberGroups = null;
511
512 for ( String group : groups.keySet() )
513 {
514 Set<String> members = groups.get( group );
515
516 if ( members == null )
517 {
518 continue;
519 }
520
521 if ( members.contains( normMember.getNormName() ) )
522 {
523 if ( memberGroups == null )
524 {
525 memberGroups = new HashSet<LdapDN>();
526 }
527
528 memberGroups.add( parseNormalized( group ) );
529 }
530 }
531
532 if ( memberGroups == null )
533 {
534 return EMPTY_GROUPS;
535 }
536
537 return memberGroups;
538 }
539
540
541 public boolean groupRenamed( LdapDN oldName, LdapDN newName )
542 {
543 Set<String> members = groups.remove( oldName.getNormName() );
544
545 if ( members != null )
546 {
547 groups.put( newName.getNormName(), members );
548
549 if ( IS_DEBUG )
550 {
551 LOG.debug( "group cache contents after renaming '{}' :\n{}", oldName.getUpName(), groups );
552 }
553
554 return true;
555 }
556
557 return false;
558 }
559 }