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
25 import java.text.ParseException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashSet;
30 import java.util.List;
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.DefaultCoreSession;
36 import org.apache.directory.server.core.DirectoryService;
37 import org.apache.directory.server.core.authn.LdapPrincipal;
38 import org.apache.directory.server.core.authz.support.ACDFEngine;
39 import org.apache.directory.server.core.entry.ClonedServerEntry;
40 import org.apache.directory.server.core.entry.ServerAttribute;
41 import org.apache.directory.server.core.entry.ServerEntry;
42 import org.apache.directory.server.core.entry.ServerEntryUtils;
43 import org.apache.directory.server.core.filtering.EntryFilter;
44 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
45 import org.apache.directory.server.core.interceptor.BaseInterceptor;
46 import org.apache.directory.server.core.interceptor.InterceptorChain;
47 import org.apache.directory.server.core.interceptor.NextInterceptor;
48 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
49 import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
50 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
51 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
52 import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
53 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
54 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
55 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
56 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
57 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
58 import org.apache.directory.server.core.interceptor.context.OperationContext;
59 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
60 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
61 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
62 import org.apache.directory.server.core.partition.ByPassConstants;
63 import org.apache.directory.server.core.subtree.SubentryInterceptor;
64 import org.apache.directory.server.schema.ConcreteNameComponentNormalizer;
65 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
66 import org.apache.directory.server.schema.registries.OidRegistry;
67 import org.apache.directory.server.schema.registries.Registries;
68 import org.apache.directory.shared.ldap.aci.ACIItem;
69 import org.apache.directory.shared.ldap.aci.ACIItemParser;
70 import org.apache.directory.shared.ldap.aci.ACITuple;
71 import org.apache.directory.shared.ldap.aci.MicroOperation;
72 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
73 import org.apache.directory.shared.ldap.constants.SchemaConstants;
74 import org.apache.directory.shared.ldap.entry.EntryAttribute;
75 import org.apache.directory.shared.ldap.entry.Modification;
76 import org.apache.directory.shared.ldap.entry.Value;
77 import org.apache.directory.shared.ldap.exception.LdapNamingException;
78 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
79 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
80 import org.apache.directory.shared.ldap.name.LdapDN;
81 import org.apache.directory.shared.ldap.schema.AttributeType;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85
86
87
88
89
90
91
92
93
94 public class AciAuthorizationInterceptor extends BaseInterceptor
95 {
96
97 private static final Logger LOG = LoggerFactory.getLogger( AciAuthorizationInterceptor.class );
98
99
100
101
102
103 private static final String AC_SUBENTRY_ATTR = "accessControlSubentries";
104
105 private static final Collection<MicroOperation> ADD_PERMS;
106 private static final Collection<MicroOperation> READ_PERMS;
107 private static final Collection<MicroOperation> COMPARE_PERMS;
108 private static final Collection<MicroOperation> SEARCH_ENTRY_PERMS;
109 private static final Collection<MicroOperation> SEARCH_ATTRVAL_PERMS;
110 private static final Collection<MicroOperation> REMOVE_PERMS;
111 private static final Collection<MicroOperation> MATCHEDNAME_PERMS;
112 private static final Collection<MicroOperation> BROWSE_PERMS;
113 private static final Collection<MicroOperation> LOOKUP_PERMS;
114 private static final Collection<MicroOperation> REPLACE_PERMS;
115 private static final Collection<MicroOperation> RENAME_PERMS;
116 private static final Collection<MicroOperation> EXPORT_PERMS;
117 private static final Collection<MicroOperation> IMPORT_PERMS;
118 private static final Collection<MicroOperation> MOVERENAME_PERMS;
119
120 static
121 {
122 Set<MicroOperation> set = new HashSet<MicroOperation>( 2 );
123 set.add( MicroOperation.BROWSE );
124 set.add( MicroOperation.RETURN_DN );
125 SEARCH_ENTRY_PERMS = Collections.unmodifiableCollection( set );
126
127 set = new HashSet<MicroOperation>( 2 );
128 set.add( MicroOperation.READ );
129 set.add( MicroOperation.BROWSE );
130 LOOKUP_PERMS = Collections.unmodifiableCollection( set );
131
132 set = new HashSet<MicroOperation>( 2 );
133 set.add( MicroOperation.ADD );
134 set.add( MicroOperation.REMOVE );
135 REPLACE_PERMS = Collections.unmodifiableCollection( set );
136
137 set = new HashSet<MicroOperation>( 2 );
138 set.add( MicroOperation.EXPORT );
139 set.add( MicroOperation.RENAME );
140 MOVERENAME_PERMS = Collections.unmodifiableCollection( set );
141
142 SEARCH_ATTRVAL_PERMS = Collections.singleton( MicroOperation.READ );
143 ADD_PERMS = Collections.singleton( MicroOperation.ADD );
144 READ_PERMS = Collections.singleton( MicroOperation.READ );
145 COMPARE_PERMS = Collections.singleton( MicroOperation.COMPARE );
146 REMOVE_PERMS = Collections.singleton( MicroOperation.REMOVE );
147 MATCHEDNAME_PERMS = Collections.singleton( MicroOperation.DISCLOSE_ON_ERROR );
148 BROWSE_PERMS = Collections.singleton( MicroOperation.BROWSE );
149 RENAME_PERMS = Collections.singleton( MicroOperation.RENAME );
150 EXPORT_PERMS = Collections.singleton( MicroOperation.EXPORT );
151 IMPORT_PERMS = Collections.singleton( MicroOperation.IMPORT );
152 }
153
154
155 private TupleCache tupleCache;
156
157
158 private GroupCache groupCache;
159
160
161 private ACIItemParser aciParser;
162
163
164 private ACDFEngine engine;
165
166
167 private InterceptorChain chain;
168
169
170 private Registries registries;
171
172
173 private AttributeTypeRegistry atRegistry;
174
175
176 private boolean enabled;
177
178
179 private String subschemaSubentryDn;
180
181 private AttributeType objectClassType;
182 private AttributeType acSubentryType;
183
184 private String subentryOid;
185
186
187 private AttributeType entryAciType;
188
189
190 private AttributeType subentryAciType;
191
192 public static final SearchControls DEFAULT_SEARCH_CONTROLS = new SearchControls();
193
194
195
196
197
198
199
200
201 public void init( DirectoryService directoryService ) throws Exception
202 {
203 super.init( directoryService );
204
205 LdapDN adminDn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
206 adminDn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
207 CoreSession adminSession = new DefaultCoreSession(
208 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
209
210 tupleCache = new TupleCache( adminSession );
211 groupCache = new GroupCache( adminSession );
212 registries = directoryService.getRegistries();
213 atRegistry = registries.getAttributeTypeRegistry();
214 OidRegistry oidRegistry = registries.getOidRegistry();
215
216
217 String objectClassOid = oidRegistry.getOid( SchemaConstants.OBJECT_CLASS_AT );
218 subentryOid = oidRegistry.getOid( SchemaConstants.SUBENTRY_OC );
219 String acSubentryOid = oidRegistry.getOid( AC_SUBENTRY_ATTR );
220 objectClassType = atRegistry.lookup( objectClassOid );
221 acSubentryType = atRegistry.lookup( acSubentryOid );
222 entryAciType = atRegistry.lookup( SchemaConstants.ENTRY_ACI_AT_OID );
223 subentryAciType = atRegistry.lookup( SchemaConstants.SUBENTRY_ACI_AT_OID );
224
225 aciParser = new ACIItemParser( new ConcreteNameComponentNormalizer( atRegistry, oidRegistry ), atRegistry.getNormalizerMapping() );
226 engine = new ACDFEngine( registries.getOidRegistry(), atRegistry );
227 chain = directoryService.getInterceptorChain();
228 enabled = directoryService.isAccessControlEnabled();
229
230
231 Value<?> subschemaSubentry =
232 directoryService.getPartitionNexus().getRootDSE( null ).
233 get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
234 LdapDN subschemaSubentryDnName = new LdapDN( (String)(subschemaSubentry.get()) );
235 subschemaSubentryDnName.normalize( atRegistry.getNormalizerMapping() );
236 subschemaSubentryDn = subschemaSubentryDnName.toNormName();
237 }
238
239
240 private void protectCriticalEntries( LdapDN dn ) throws Exception
241 {
242 LdapDN principalDn = getPrincipal().getJndiName();
243
244 if ( dn.isEmpty() )
245 {
246 String msg = "The rootDSE cannot be deleted, moved or renamed!";
247 LOG.error( msg );
248 throw new LdapNoPermissionException( msg );
249 }
250
251 if ( isTheAdministrator( dn ) )
252 {
253 String msg = "User '" + principalDn.getUpName();
254 msg += "' does not have permission to move or rename the admin";
255 msg += " account. No one not even the admin can del, move or";
256 msg += " rename " + dn.getUpName() + "!";
257 LOG.error( msg );
258 throw new LdapNoPermissionException( msg );
259 }
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277 private void addPerscriptiveAciTuples( OperationContext opContext, Collection<ACITuple> tuples, LdapDN dn,
278 ServerEntry entry ) throws Exception
279 {
280 EntryAttribute oc = null;
281
282 if ( entry instanceof ClonedServerEntry )
283 {
284 oc = ((ClonedServerEntry)entry).getOriginalEntry().get( objectClassType );
285 }
286 else
287 {
288 oc = entry.get( objectClassType );
289 }
290
291
292
293
294
295
296
297
298
299
300 if ( oc.contains( SchemaConstants.SUBENTRY_OC ) || oc.contains( subentryOid ) )
301 {
302 LdapDN parentDn = ( LdapDN ) dn.clone();
303 parentDn.remove( dn.size() - 1 );
304 entry = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS );
305 }
306
307 EntryAttribute subentries = entry.get( acSubentryType );
308
309 if ( subentries == null )
310 {
311 return;
312 }
313
314 for ( Value<?> value:subentries )
315 {
316 String subentryDn = ( String ) value.get();
317 tuples.addAll( tupleCache.getACITuples( subentryDn ) );
318 }
319 }
320
321
322
323
324
325
326
327
328
329
330 private void addEntryAciTuples( Collection<ACITuple> tuples, ServerEntry entry ) throws Exception
331 {
332 EntryAttribute entryAci = entry.get( entryAciType );
333
334 if ( entryAci == null )
335 {
336 return;
337 }
338
339 for ( Value<?> value:entryAci )
340 {
341 String aciString = ( String ) value.get();
342 ACIItem item;
343
344 try
345 {
346 item = aciParser.parse( aciString );
347 }
348 catch ( ParseException e )
349 {
350 String msg = "failed to parse entryACI: " + aciString;
351 LOG.error( msg, e );
352 throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
353 }
354
355 tuples.addAll( item.toTuples() );
356 }
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370 private void addSubentryAciTuples( OperationContext opContext, Collection<ACITuple> tuples, LdapDN dn, ServerEntry entry )
371 throws Exception
372 {
373
374 if ( !entry.contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.SUBENTRY_OC ) )
375 {
376 return;
377 }
378
379
380
381 LdapDN parentDn = ( LdapDN ) dn.clone();
382 parentDn.remove( dn.size() - 1 );
383 ServerEntry administrativeEntry = opContext.lookup( parentDn, ByPassConstants.LOOKUP_BYPASS ).getOriginalEntry();
384
385 EntryAttribute subentryAci = administrativeEntry.get( subentryAciType );
386
387 if ( subentryAci == null )
388 {
389 return;
390 }
391
392 for ( Value<?> value:subentryAci )
393 {
394 String aciString = ( String ) value.get();
395 ACIItem item;
396
397 try
398 {
399 item = aciParser.parse( aciString );
400 }
401 catch ( ParseException e )
402 {
403 String msg = "failed to parse subentryACI: " + aciString;
404 LOG.error( msg, e );
405 throw new LdapNamingException( msg, ResultCodeEnum.OPERATIONS_ERROR );
406 }
407
408 tuples.addAll( item.toTuples() );
409 }
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception
435 {
436
437 LdapPrincipal principal = addContext.getSession().getEffectivePrincipal();
438 LdapDN principalDn = principal.getJndiName();
439
440 ServerEntry serverEntry = addContext.getEntry();
441
442
443 LdapDN name = addContext.getDn();
444
445
446 if ( !enabled )
447 {
448 next.add( addContext );
449 return;
450 }
451
452
453 if ( isPrincipalAnAdministrator( principalDn ) )
454 {
455 next.add( addContext );
456 tupleCache.subentryAdded( name, serverEntry );
457 groupCache.groupAdded( name, serverEntry );
458 return;
459 }
460
461
462 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
463 ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( name, serverEntry );
464
465 for ( EntryAttribute attribute:serverEntry )
466 {
467 subentryAttrs.put( attribute );
468 }
469
470
471 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
472 Collection<ACITuple> tuples = new HashSet<ACITuple>();
473
474
475
476 addPerscriptiveAciTuples( addContext, tuples, name, subentryAttrs );
477 addSubentryAciTuples( addContext, tuples, name, subentryAttrs );
478
479
480 engine.checkPermission( registries, addContext, userGroups, principalDn, principal.getAuthenticationLevel(), name, null, null,
481 ADD_PERMS, tuples, subentryAttrs, null );
482
483
484 for ( EntryAttribute attribute:serverEntry )
485 {
486 for ( Value<?> value:attribute )
487 {
488 engine.checkPermission( registries, addContext, userGroups, principalDn,
489 principal.getAuthenticationLevel(), name, attribute.getUpId(), value,
490 ADD_PERMS, tuples, serverEntry, null );
491 }
492 }
493
494
495 next.add( addContext );
496
497
498
499 tupleCache.subentryAdded( name, serverEntry );
500 groupCache.groupAdded( name, serverEntry );
501 }
502
503
504 private boolean isTheAdministrator( LdapDN normalizedDn )
505 {
506 return normalizedDn.getNormName().equals( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
507 }
508
509
510 public void delete( NextInterceptor next, DeleteOperationContext deleteContext ) throws Exception
511 {
512 LdapDN name = deleteContext.getDn();
513
514 LdapPrincipal principal = deleteContext.getSession().getEffectivePrincipal();
515 LdapDN principalDn = principal.getJndiName();
516
517
518 if ( ! enabled )
519 {
520 next.delete( deleteContext );
521 return;
522 }
523
524 ClonedServerEntry entry = deleteContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
525
526 protectCriticalEntries( name );
527
528
529 if ( isPrincipalAnAdministrator( principalDn ) )
530 {
531 next.delete( deleteContext );
532 tupleCache.subentryDeleted( name, entry );
533 groupCache.groupDeleted( name, entry );
534 return;
535 }
536
537 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
538 Collection<ACITuple> tuples = new HashSet<ACITuple>();
539 addPerscriptiveAciTuples( deleteContext, tuples, name, entry.getOriginalEntry() );
540 addEntryAciTuples( tuples, entry );
541 addSubentryAciTuples( deleteContext, tuples, name, entry );
542
543 engine.checkPermission( registries, deleteContext, userGroups, principalDn,
544 principal.getAuthenticationLevel(), name, null, null, REMOVE_PERMS, tuples, entry, null );
545
546 next.delete( deleteContext );
547 tupleCache.subentryDeleted( name, entry );
548 groupCache.groupDeleted( name, entry );
549 }
550
551
552 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
553 {
554 LdapDN name = opContext.getDn();
555
556
557 ClonedServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
558
559 LdapPrincipal principal = opContext.getSession().getEffectivePrincipal();
560 LdapDN principalDn = principal.getJndiName();
561
562
563 if ( !enabled )
564 {
565 next.modify( opContext );
566 return;
567 }
568
569 List<Modification> mods = opContext.getModItems();
570
571
572 if ( isPrincipalAnAdministrator( principalDn ) )
573 {
574 next.modify( opContext );
575
576
577
578 ServerEntry modifiedEntry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
579 tupleCache.subentryModified( name, mods, modifiedEntry );
580 groupCache.groupModified( name, mods, entry, registries );
581 return;
582 }
583
584 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
585 Collection<ACITuple> tuples = new HashSet<ACITuple>();
586 addPerscriptiveAciTuples( opContext, tuples, name, entry.getOriginalEntry() );
587 addEntryAciTuples( tuples, entry );
588 addSubentryAciTuples( opContext, tuples, name, entry );
589
590 engine.checkPermission( registries, opContext, userGroups, principalDn,
591 principal.getAuthenticationLevel(), name, null, null,
592 Collections.singleton( MicroOperation.MODIFY ), tuples, entry, null );
593
594 Collection<MicroOperation> perms = null;
595 ServerEntry entryView = ( ServerEntry ) entry.clone();
596
597 for ( Modification mod : mods )
598 {
599 ServerAttribute attr = (ServerAttribute)mod.getAttribute();
600
601 switch ( mod.getOperation() )
602 {
603 case ADD_ATTRIBUTE :
604 perms = ADD_PERMS;
605
606
607 if ( entry.get( attr.getId() ) == null )
608 {
609
610 engine.checkPermission( registries, opContext, userGroups, principalDn, principal.getAuthenticationLevel(), name,
611 attr.getId(), null, perms, tuples, entry, null );
612 }
613
614 break;
615
616 case REMOVE_ATTRIBUTE :
617 perms = REMOVE_PERMS;
618 EntryAttribute entryAttr = entry.get( attr.getId() );
619
620 if ( entryAttr != null )
621 {
622
623 if ( entryAttr.size() == 1 )
624 {
625
626 engine.checkPermission( registries, opContext, userGroups, principalDn,
627 principal.getAuthenticationLevel(), name, attr.getId(),
628 null, perms, tuples, entry, null );
629 }
630 }
631
632 break;
633
634 case REPLACE_ATTRIBUTE :
635 perms = REPLACE_PERMS;
636 break;
637 }
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652 entryView = ServerEntryUtils.getTargetEntry( mod, entryView, registries );
653
654 for ( Value<?> value:attr )
655 {
656 engine.checkPermission( registries, opContext, userGroups, principalDn,
657 principal.getAuthenticationLevel(), name, attr.getId(), value,
658 perms, tuples, entry, entryView );
659 }
660 }
661
662
663
664 next.modify( opContext );
665
666
667
668 ServerEntry modifiedEntry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
669 tupleCache.subentryModified( name, mods, modifiedEntry );
670 groupCache.groupModified( name, mods, entry, registries );
671 }
672
673
674 public boolean hasEntry( NextInterceptor next, EntryOperationContext entryContext ) throws Exception
675 {
676 LdapDN name = entryContext.getDn();
677 if ( ! enabled )
678 {
679 return name.size() == 0 || next.hasEntry( entryContext );
680 }
681
682 boolean answer = next.hasEntry( entryContext );
683
684
685 if ( name.size() == 0 )
686 {
687
688
689 return answer;
690 }
691
692
693 LdapPrincipal principal = entryContext.getSession().getEffectivePrincipal();
694 LdapDN principalDn = principal.getJndiName();
695 if ( isPrincipalAnAdministrator( principalDn ) )
696 {
697 return answer;
698 }
699
700 ClonedServerEntry entry = entryContext.lookup( name, ByPassConstants.HAS_ENTRY_BYPASS );
701 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
702 Collection<ACITuple> tuples = new HashSet<ACITuple>();
703 addPerscriptiveAciTuples( entryContext, tuples, name, entry.getOriginalEntry() );
704 addEntryAciTuples( tuples, entry.getOriginalEntry() );
705 addSubentryAciTuples( entryContext, tuples, name, entry.getOriginalEntry() );
706
707
708 engine.checkPermission( registries, entryContext, userGroups, principalDn,
709 principal.getAuthenticationLevel(), name, null, null,
710 BROWSE_PERMS, tuples, entry.getOriginalEntry(), null );
711
712 return next.hasEntry( entryContext );
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731 private void checkLookupAccess( LookupOperationContext lookupContext, ServerEntry entry ) throws Exception
732 {
733
734 if ( lookupContext.getDn().toString().trim().equals( "" ) )
735 {
736 return;
737 }
738
739 LdapPrincipal principal = lookupContext.getSession().getEffectivePrincipal();
740 LdapDN userName = principal.getJndiName();
741 Set<LdapDN> userGroups = groupCache.getGroups( userName.toNormName() );
742 Collection<ACITuple> tuples = new HashSet<ACITuple>();
743 addPerscriptiveAciTuples( lookupContext, tuples, lookupContext.getDn(), entry );
744 addEntryAciTuples( tuples, entry );
745 addSubentryAciTuples( lookupContext, tuples, lookupContext.getDn(), entry );
746
747
748 engine.checkPermission( registries, lookupContext, userGroups, userName, principal.getAuthenticationLevel(),
749 lookupContext.getDn(), null, null,
750 LOOKUP_PERMS, tuples, entry, null );
751
752
753 for ( EntryAttribute attribute:entry )
754 {
755
756 for ( Value<?> value:attribute )
757 {
758 engine.checkPermission(
759 registries,
760 lookupContext,
761 userGroups,
762 userName,
763 principal.getAuthenticationLevel(),
764 lookupContext.getDn(),
765 attribute.getUpId(),
766 value,
767 READ_PERMS,
768 tuples,
769 entry,
770 null );
771 }
772 }
773 }
774
775
776 public ClonedServerEntry lookup( NextInterceptor next, LookupOperationContext lookupContext ) throws Exception
777 {
778 LdapPrincipal principal = lookupContext.getSession().getEffectivePrincipal();
779 LdapDN principalDn = principal.getJndiName();
780
781 if ( !principalDn.isNormalized() )
782 {
783 principalDn.normalize( atRegistry.getNormalizerMapping() );
784 }
785
786 if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
787 {
788 return next.lookup( lookupContext );
789 }
790
791 lookupContext.setByPassed( ByPassConstants.LOOKUP_BYPASS );
792 ServerEntry entry = lookupContext.getSession().getDirectoryService()
793 .getOperationManager().lookup( lookupContext );
794
795 checkLookupAccess( lookupContext, entry );
796 return next.lookup( lookupContext );
797 }
798
799
800 public void rename( NextInterceptor next, RenameOperationContext renameContext ) throws Exception
801 {
802 LdapDN name = renameContext.getDn();
803
804 ClonedServerEntry entry = renameContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
805
806 LdapPrincipal principal = renameContext.getSession().getEffectivePrincipal();
807 LdapDN principalDn = principal.getJndiName();
808 LdapDN newName = ( LdapDN ) name.clone();
809 newName.remove( name.size() - 1 );
810
811 newName.add( renameContext.getNewRdn() );
812
813
814 if ( !enabled )
815 {
816 next.rename( renameContext );
817 return;
818 }
819
820 protectCriticalEntries( name );
821
822
823 if ( isPrincipalAnAdministrator( principalDn ) )
824 {
825 next.rename( renameContext );
826 tupleCache.subentryRenamed( name, newName );
827
828
829 groupCache.groupRenamed( name, newName );
830
831 return;
832 }
833
834 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
835 Collection<ACITuple> tuples = new HashSet<ACITuple>();
836 addPerscriptiveAciTuples( renameContext, tuples, name, entry.getOriginalEntry() );
837 addEntryAciTuples( tuples, entry );
838 addSubentryAciTuples( renameContext, tuples, name, entry );
839
840 engine.checkPermission( registries, renameContext, userGroups, principalDn,
841 principal.getAuthenticationLevel(), name, null, null,
842 RENAME_PERMS, tuples, entry, null );
843
844 next.rename( renameContext );
845 tupleCache.subentryRenamed( name, newName );
846 groupCache.groupRenamed( name, newName );
847 }
848
849
850 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext moveAndRenameContext )
851 throws Exception
852 {
853 LdapDN oriChildName = moveAndRenameContext.getDn();
854 LdapDN newParentName = moveAndRenameContext.getParent();
855
856 ClonedServerEntry entry = moveAndRenameContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
857
858 LdapPrincipal principal = moveAndRenameContext.getSession().getEffectivePrincipal();
859 LdapDN principalDn = principal.getJndiName();
860 LdapDN newName = ( LdapDN ) newParentName.clone();
861 newName.add( moveAndRenameContext.getNewRdn().getUpName() );
862
863
864 if ( !enabled )
865 {
866 next.moveAndRename( moveAndRenameContext );
867 return;
868 }
869
870 protectCriticalEntries( oriChildName );
871
872
873 if ( isPrincipalAnAdministrator( principalDn ) )
874 {
875 next.moveAndRename( moveAndRenameContext );
876 tupleCache.subentryRenamed( oriChildName, newName );
877 groupCache.groupRenamed( oriChildName, newName );
878 return;
879 }
880
881 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
882 Collection<ACITuple> tuples = new HashSet<ACITuple>();
883 addPerscriptiveAciTuples( moveAndRenameContext, tuples, oriChildName, entry.getOriginalEntry() );
884 addEntryAciTuples( tuples, entry );
885 addSubentryAciTuples( moveAndRenameContext, tuples, oriChildName, entry );
886
887 engine.checkPermission( registries, moveAndRenameContext, userGroups,
888 principalDn, principal.getAuthenticationLevel(), oriChildName, null,
889 null, MOVERENAME_PERMS, tuples, entry, null );
890
891
892
893
894
895
896
897 ClonedServerEntry importedEntry = moveAndRenameContext.lookup( oriChildName,
898 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
899
900
901
902
903
904
905 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor ) chain.get( SubentryInterceptor.class.getName() );
906 ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( newName, importedEntry );
907
908 for ( EntryAttribute attribute:importedEntry )
909 {
910 subentryAttrs.put( attribute );
911 }
912
913 Collection<ACITuple> destTuples = new HashSet<ACITuple>();
914
915 addPerscriptiveAciTuples( moveAndRenameContext, destTuples, newName, subentryAttrs );
916
917
918 engine.checkPermission( registries, moveAndRenameContext, userGroups, principalDn,
919 principal.getAuthenticationLevel(), newName, null,
920 null, IMPORT_PERMS, destTuples, subentryAttrs, null );
921
922
923 next.moveAndRename( moveAndRenameContext );
924 tupleCache.subentryRenamed( oriChildName, newName );
925 groupCache.groupRenamed( oriChildName, newName );
926 }
927
928
929 public void move( NextInterceptor next, MoveOperationContext moveContext ) throws Exception
930 {
931 LdapDN oriChildName = moveContext.getDn();
932 LdapDN newParentName = moveContext.getParent();
933
934
935 ClonedServerEntry entry = moveContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
936
937 LdapDN newName = ( LdapDN ) newParentName.clone();
938 newName.add( oriChildName.get( oriChildName.size() - 1 ) );
939 LdapPrincipal principal = moveContext.getSession().getEffectivePrincipal();
940 LdapDN principalDn = principal.getJndiName();
941
942
943 if ( !enabled )
944 {
945 next.move( moveContext );
946 return;
947 }
948
949 protectCriticalEntries( oriChildName);
950
951
952 if ( isPrincipalAnAdministrator( principalDn ) )
953 {
954 next.move( moveContext );
955 tupleCache.subentryRenamed( oriChildName, newName );
956 groupCache.groupRenamed( oriChildName, newName );
957 return;
958 }
959
960 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
961 Collection<ACITuple> tuples = new HashSet<ACITuple>();
962 addPerscriptiveAciTuples( moveContext, tuples, oriChildName, entry.getOriginalEntry() );
963 addEntryAciTuples( tuples, entry );
964 addSubentryAciTuples( moveContext, tuples, oriChildName, entry );
965
966 engine.checkPermission( registries, moveContext, userGroups, principalDn,
967 principal.getAuthenticationLevel(), oriChildName, null,
968 null, EXPORT_PERMS, tuples, entry, null );
969
970
971
972
973
974
975 ServerEntry importedEntry = moveContext.lookup( oriChildName,
976 ByPassConstants.LOOKUP_EXCLUDING_OPR_ATTRS_BYPASS );
977
978
979
980
981
982
983 SubentryInterceptor subentryInterceptor = ( SubentryInterceptor )
984 chain.get( SubentryInterceptor.class.getName() );
985 ServerEntry subentryAttrs = subentryInterceptor.getSubentryAttributes( newName, importedEntry );
986
987 for ( EntryAttribute attribute:importedEntry )
988 {
989 subentryAttrs.put( attribute );
990 }
991
992 Collection<ACITuple> destTuples = new HashSet<ACITuple>();
993
994 addPerscriptiveAciTuples( moveContext, destTuples, newName, subentryAttrs );
995
996
997 engine.checkPermission( registries, moveContext, userGroups, principalDn,
998 principal.getAuthenticationLevel(), newName, null,
999 null, IMPORT_PERMS, destTuples, subentryAttrs, null );
1000
1001 next.move( moveContext );
1002 tupleCache.subentryRenamed( oriChildName, newName );
1003 groupCache.groupRenamed( oriChildName, newName );
1004 }
1005
1006
1007 public EntryFilteringCursor list( NextInterceptor next, ListOperationContext opContext ) throws Exception
1008 {
1009 LdapPrincipal user = opContext.getSession().getEffectivePrincipal();
1010 EntryFilteringCursor cursor = next.list( opContext );
1011
1012 if ( isPrincipalAnAdministrator( user.getJndiName() ) || !enabled )
1013 {
1014 return cursor;
1015 }
1016
1017 AuthorizationFilter authzFilter = new AuthorizationFilter();
1018 cursor.addEntryFilter( authzFilter );
1019 return cursor;
1020 }
1021
1022
1023 public EntryFilteringCursor search( NextInterceptor next, SearchOperationContext opContext ) throws Exception
1024 {
1025 LdapPrincipal user = opContext.getSession().getEffectivePrincipal();
1026 LdapDN principalDn = user.getJndiName();
1027 EntryFilteringCursor cursor = next.search( opContext );
1028
1029 boolean isSubschemaSubentryLookup = subschemaSubentryDn.equals( opContext.getDn().getNormName() );
1030 SearchControls searchCtls = opContext.getSearchControls();
1031 boolean isRootDSELookup = opContext.getDn().size() == 0 && searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
1032
1033 if ( isPrincipalAnAdministrator( principalDn ) || !enabled || isRootDSELookup || isSubschemaSubentryLookup )
1034 {
1035 return cursor;
1036 }
1037
1038 cursor.addEntryFilter( new AuthorizationFilter() );
1039 return cursor;
1040 }
1041
1042
1043 public final boolean isPrincipalAnAdministrator( LdapDN principalDn )
1044 {
1045 return groupCache.isPrincipalAnAdministrator( principalDn );
1046 }
1047
1048
1049 public boolean compare( NextInterceptor next, CompareOperationContext opContext ) throws Exception
1050 {
1051 LdapDN name = opContext.getDn();
1052 String oid = opContext.getOid();
1053 Value<?> value = ( Value<?> ) opContext.getValue();
1054
1055 ClonedServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
1056
1057 LdapPrincipal principal = opContext.getSession().getEffectivePrincipal();
1058 LdapDN principalDn = principal.getJndiName();
1059
1060 if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
1061 {
1062 return next.compare( opContext );
1063 }
1064
1065 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toNormName() );
1066 Collection<ACITuple> tuples = new HashSet<ACITuple>();
1067 addPerscriptiveAciTuples( opContext, tuples, name, entry.getOriginalEntry() );
1068 addEntryAciTuples( tuples, entry );
1069 addSubentryAciTuples( opContext, tuples, name, entry );
1070
1071 engine.checkPermission( registries, opContext, userGroups, principalDn,
1072 principal.getAuthenticationLevel(), name, null, null,
1073 READ_PERMS, tuples, entry, null );
1074 engine.checkPermission( registries, opContext, userGroups, principalDn,
1075 principal.getAuthenticationLevel(), name, oid, value,
1076 COMPARE_PERMS, tuples, entry, null );
1077
1078 return next.compare( opContext );
1079 }
1080
1081
1082 public LdapDN getMatchedName ( NextInterceptor next, GetMatchedNameOperationContext opContext ) throws Exception
1083 {
1084
1085 LdapPrincipal principal = opContext.getSession().getEffectivePrincipal();
1086 LdapDN principalDn = principal.getJndiName();
1087
1088 if ( isPrincipalAnAdministrator( principalDn ) || !enabled )
1089 {
1090 return next.getMatchedName( opContext );
1091 }
1092
1093
1094 ClonedServerEntry entry;
1095 LdapDN matched = next.getMatchedName( opContext );
1096
1097
1098
1099
1100 while ( matched.size() > 0 )
1101 {
1102 entry = opContext.lookup( matched, ByPassConstants.GETMATCHEDDN_BYPASS );
1103
1104 Set<LdapDN> userGroups = groupCache.getGroups( principalDn.toString() );
1105 Collection<ACITuple> tuples = new HashSet<ACITuple>();
1106 addPerscriptiveAciTuples( opContext, tuples, matched, entry.getOriginalEntry() );
1107 addEntryAciTuples( tuples, entry );
1108 addSubentryAciTuples( opContext, tuples, matched, entry );
1109
1110 if ( engine.hasPermission( registries, opContext, userGroups, principalDn,
1111 principal.getAuthenticationLevel(), matched, null,
1112 null, MATCHEDNAME_PERMS, tuples, entry, null ) )
1113 {
1114 return matched;
1115 }
1116
1117 matched.remove( matched.size() - 1 );
1118 }
1119
1120 return matched;
1121 }
1122
1123
1124 public void cacheNewGroup( LdapDN name, ServerEntry entry ) throws Exception
1125 {
1126 groupCache.groupAdded( name, entry );
1127 }
1128
1129
1130 private boolean filter( OperationContext opContext, LdapDN normName, ClonedServerEntry clonedEntry )
1131 throws Exception
1132 {
1133
1134
1135
1136
1137
1138
1139 LdapPrincipal principal = opContext.getSession().getEffectivePrincipal();
1140 LdapDN userDn = principal.getJndiName();
1141 Set<LdapDN> userGroups = groupCache.getGroups( userDn.toNormName() );
1142 Collection<ACITuple> tuples = new HashSet<ACITuple>();
1143 addPerscriptiveAciTuples( opContext, tuples, normName, clonedEntry.getOriginalEntry() );
1144 addEntryAciTuples( tuples, clonedEntry.getOriginalEntry() );
1145 addSubentryAciTuples( opContext, tuples, normName, clonedEntry.getOriginalEntry() );
1146
1147 if ( !engine.hasPermission(
1148 registries,
1149 opContext,
1150 userGroups,
1151 userDn,
1152 principal.getAuthenticationLevel(),
1153 normName,
1154 null,
1155 null,
1156 SEARCH_ENTRY_PERMS,
1157 tuples,
1158 clonedEntry.getOriginalEntry(),
1159 null ) )
1160 {
1161 return false;
1162 }
1163
1164
1165
1166
1167
1168
1169
1170
1171 List<AttributeType> attributeToRemove = new ArrayList<AttributeType>();
1172
1173 for ( AttributeType attributeType:clonedEntry.getAttributeTypes() )
1174 {
1175
1176 String id = attributeType.getName();
1177 EntryAttribute attr = clonedEntry.get( attributeType );
1178
1179 if ( !engine.hasPermission(
1180 registries,
1181 opContext,
1182 userGroups,
1183 userDn,
1184 principal.getAuthenticationLevel(),
1185 normName,
1186 id,
1187 null,
1188 SEARCH_ATTRVAL_PERMS,
1189 tuples,
1190 clonedEntry,
1191 null ) )
1192 {
1193 attributeToRemove.add( attributeType );
1194
1195 continue;
1196 }
1197
1198 List<Value<?>> valueToRemove = new ArrayList<Value<?>>();
1199
1200
1201 for ( Value<?> value:attr )
1202 {
1203 if ( !engine.hasPermission(
1204 registries,
1205 opContext,
1206 userGroups,
1207 userDn,
1208 principal.getAuthenticationLevel(),
1209 normName,
1210 attr.getUpId(),
1211 value,
1212 SEARCH_ATTRVAL_PERMS,
1213 tuples,
1214 clonedEntry,
1215 null ) )
1216 {
1217 valueToRemove.add( value );
1218 }
1219 }
1220
1221 for ( Value<?> value:valueToRemove )
1222 {
1223 attr.remove( value );
1224 }
1225
1226 if ( attr.size() == 0 )
1227 {
1228 attributeToRemove.add( attributeType );
1229 }
1230 }
1231
1232 for ( AttributeType attributeType:attributeToRemove )
1233 {
1234 clonedEntry.removeAttributes( attributeType );
1235 }
1236
1237 return true;
1238 }
1239
1240
1241
1242
1243
1244 class AuthorizationFilter implements EntryFilter
1245 {
1246 public boolean accept( SearchingOperationContext operationContext, ClonedServerEntry entry )
1247 throws Exception
1248 {
1249 LdapDN normName = entry.getDn().normalize( atRegistry.getNormalizerMapping() );
1250 return filter( operationContext, normName, entry );
1251 }
1252 }
1253 }