1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.subtree;
18
19
20 import org.apache.ldap.server.DirectoryServiceConfiguration;
21 import org.apache.ldap.server.interceptor.BaseInterceptor;
22 import org.apache.ldap.server.interceptor.NextInterceptor;
23 import org.apache.ldap.server.enumeration.SearchResultFilter;
24 import org.apache.ldap.server.enumeration.SearchResultFilteringEnumeration;
25 import org.apache.ldap.server.invocation.InvocationStack;
26 import org.apache.ldap.server.invocation.Invocation;
27 import org.apache.ldap.server.configuration.InterceptorConfiguration;
28 import org.apache.ldap.server.partition.DirectoryPartitionNexus;
29 import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
30 import org.apache.ldap.common.message.SubentryRequestControl;
31 import org.apache.ldap.common.message.ResultCodeEnum;
32 import org.apache.ldap.common.message.LockableAttributesImpl;
33 import org.apache.ldap.common.message.LockableAttributeImpl;
34 import org.apache.ldap.common.filter.*;
35 import org.apache.ldap.common.subtree.SubtreeSpecificationParser;
36 import org.apache.ldap.common.subtree.SubtreeSpecification;
37 import org.apache.ldap.common.name.DnParser;
38 import org.apache.ldap.common.name.LdapName;
39 import org.apache.ldap.common.exception.LdapNoSuchAttributeException;
40 import org.apache.ldap.common.exception.LdapInvalidAttributeValueException;
41 import org.apache.ldap.common.exception.LdapSchemaViolationException;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import javax.naming.ldap.Control;
46 import javax.naming.ldap.LdapContext;
47 import javax.naming.directory.*;
48 import javax.naming.NamingException;
49 import javax.naming.NamingEnumeration;
50 import javax.naming.Name;
51 import java.util.*;
52
53
54 /***
55 * The Subentry interceptor service which is responsible for filtering
56 * out subentries on search operations and injecting operational attributes
57 *
58 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
59 * @version $Rev$
60 */
61 public class SubentryService extends BaseInterceptor
62 {
63 /*** the subentry control OID */
64 private static final String SUBENTRY_CONTROL = "1.3.6.1.4.1.4203.1.10.1";
65 /*** the objectClass value for a subentry */
66 private static final String SUBENTRY_OBJECTCLASS = "subentry";
67 /*** the objectClass OID for a subentry */
68 private static final String SUBENTRY_OBJECTCLASS_OID = "2.5.17.0";
69
70 public static final String AUTONOUMOUS_AREA = "autonomousArea";
71 public static final String AUTONOUMOUS_AREA_SUBENTRY = "autonomousAreaSubentry";
72
73 public static final String AC_AREA = "accessControlSpecificArea";
74 public static final String AC_INNERAREA = "accessControlInnerArea";
75 public static final String AC_SUBENTRY = "accessControlSubentries";
76
77 public static final String SCHEMA_AREA = "subschemaAdminSpecificArea";
78 public static final String SCHEMA_AREA_SUBENTRY = "subschemaSubentry";
79
80 public static final String COLLECTIVE_AREA = "collectiveAttributeSpecificArea";
81 public static final String COLLECTIVE_ATTRIBUTE_SUBENTRIES = "collectiveAttributeSubentries";
82
83 public static final String COLLECTIVE_INNERAREA = "collectiveAttributeInnerArea";
84
85 public static final String[] SUBENTRY_OPATTRS = {
86 AUTONOUMOUS_AREA_SUBENTRY,
87 AC_SUBENTRY,
88 SCHEMA_AREA_SUBENTRY,
89 COLLECTIVE_ATTRIBUTE_SUBENTRIES
90 };
91
92 /***
93 * the search result filter to filter out subentries based on objectClass values.
94 */
95 private static final SearchResultFilter SUBENTRY_FILTER = new SearchResultFilter()
96 {
97 public boolean accept( Invocation invocation, SearchResult result, SearchControls controls )
98 {
99 Attribute objectClasses = result.getAttributes().get( "objectClass" );
100
101 if ( objectClasses == null )
102 {
103 return true;
104 }
105
106 return !( objectClasses.contains(SUBENTRY_OBJECTCLASS) || objectClasses.contains(SUBENTRY_OBJECTCLASS_OID) );
107 }
108 };
109
110 private static final Logger log = LoggerFactory.getLogger( SubentryService.class );
111
112 /*** the hash mapping the DN of a subentry to its SubtreeSpecification */
113 private final Map subtrees = new HashMap();
114 private DirectoryServiceConfiguration factoryCfg;
115 private DnParser dnParser;
116 private SubtreeSpecificationParser ssParser;
117 private SubtreeEvaluator evaluator;
118 private DirectoryPartitionNexus nexus;
119
120
121 public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
122 {
123 super.init( factoryCfg, cfg );
124 this.nexus = factoryCfg.getPartitionNexus();
125 this.factoryCfg = factoryCfg;
126 ConcreteNameComponentNormalizer ncn = new ConcreteNameComponentNormalizer(
127 factoryCfg.getGlobalRegistries().getAttributeTypeRegistry() );
128 ssParser = new SubtreeSpecificationParser( ncn );
129 dnParser = new DnParser( ncn );
130 evaluator = new SubtreeEvaluator( factoryCfg.getGlobalRegistries().getOidRegistry() );
131
132
133 Iterator suffixes = this.nexus.listSuffixes( true );
134 ExprNode filter = new SimpleNode( "objectclass", "subentry", LeafNode.EQUALITY );
135 SearchControls controls = new SearchControls();
136 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
137 controls.setReturningAttributes( new String[] { "subtreeSpecification" } );
138
139
140 while ( suffixes.hasNext() )
141 {
142 Name suffix = dnParser.parse( ( String ) suffixes.next() );
143 NamingEnumeration subentries = nexus.search( suffix, factoryCfg.getEnvironment(), filter, controls );
144 while ( subentries.hasMore() )
145 {
146 SearchResult result = ( SearchResult ) subentries.next();
147 Attributes subentry = result.getAttributes();
148 String dn = result.getName();
149 String subtree = ( String ) subentry.get( "subtreeSpecification" ).get();
150 SubtreeSpecification ss;
151
152 try
153 {
154 ss = ssParser.parse( subtree );
155 }
156 catch ( Exception e )
157 {
158 log.warn( "Failed while parsing subtreeSpecification for " + dn );
159 continue;
160 }
161
162 subtrees.put( dnParser.parse( dn ).toString(), ss );
163 }
164 }
165 }
166
167
168
169
170
171
172
173 public NamingEnumeration list( NextInterceptor nextInterceptor, Name base ) throws NamingException
174 {
175 NamingEnumeration e = nextInterceptor.list( base );
176 Invocation invocation = InvocationStack.getInstance().peek();
177
178 if ( ! isSubentryVisible( invocation ) )
179 {
180 return new SearchResultFilteringEnumeration( e, new SearchControls(), invocation, SUBENTRY_FILTER );
181 }
182
183 return e;
184 }
185
186
187 public NamingEnumeration search( NextInterceptor nextInterceptor, Name base, Map env, ExprNode filter,
188 SearchControls searchCtls ) throws NamingException
189 {
190 NamingEnumeration e = nextInterceptor.search( base, env, filter, searchCtls );
191 Invocation invocation = InvocationStack.getInstance().peek();
192
193
194 if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
195 {
196 return e;
197 }
198
199
200 if ( ! isSubentryVisible( invocation ) )
201 {
202 return new SearchResultFilteringEnumeration( e, searchCtls, invocation, SUBENTRY_FILTER );
203 }
204
205 return e;
206 }
207
208
209 /***
210 * Checks to see if subentries for the search and list operations should be
211 * made visible based on the availability of the search request control
212 *
213 * @param invocation
214 * @return true if subentries should be visible, false otherwise
215 * @throws NamingException if there are problems accessing request controls
216 */
217 private boolean isSubentryVisible( Invocation invocation ) throws NamingException
218 {
219 Control[] reqControls = ( ( LdapContext ) invocation.getCaller() ).getRequestControls();
220
221 if ( reqControls == null || reqControls.length <= 0 )
222 {
223 return false;
224 }
225
226
227 for ( int ii = 0; ii < reqControls.length; ii++ )
228 {
229
230 if ( reqControls[ii].getID().equals( SUBENTRY_CONTROL ) )
231 {
232 SubentryRequestControl subentryControl = ( SubentryRequestControl ) reqControls[ii];
233 return subentryControl.getSubentryVisibility();
234 }
235 }
236
237 return false;
238 }
239
240
241
242
243
244
245
246 /***
247 * Evaluates the set of subentry subtrees upon an entry and returns the
248 * operational subentry attributes that will be added to the entry if
249 * added at the dn specified.
250 *
251 * @param dn the normalized distinguished name of the entry
252 * @param entryAttrs the entry attributes are generated for
253 * @return the set of subentry op attrs for an entry
254 * @throws NamingException if there are problems accessing entry information
255 */
256 public Attributes getSubentryAttributes( Name dn, Attributes entryAttrs ) throws NamingException
257 {
258 Attributes subentryAttrs = new LockableAttributesImpl();
259 Attribute objectClasses = entryAttrs.get( "objectClass" );
260 Iterator list = subtrees.keySet().iterator();
261 while ( list.hasNext() )
262 {
263 String subentryDnStr = ( String ) list.next();
264 Name subentryDn = new LdapName( subentryDnStr );
265 Name apDn = ( Name ) subentryDn.clone();
266 apDn.remove( apDn.size() - 1 );
267 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( subentryDn );
268
269 if ( evaluator.evaluate( ss, apDn, dn, objectClasses ) )
270 {
271 Attribute administrativeRole = nexus.lookup( apDn ).get( "administrativeRole" );
272 NamingEnumeration roles = administrativeRole.getAll();
273 while ( roles.hasMore() )
274 {
275 Attribute operational;
276 String role = ( String ) roles.next();
277
278 if ( role.equalsIgnoreCase( AUTONOUMOUS_AREA ) )
279 {
280 operational = subentryAttrs.get( AUTONOUMOUS_AREA_SUBENTRY );
281 if ( operational == null )
282 {
283 operational = new LockableAttributeImpl( AUTONOUMOUS_AREA_SUBENTRY );
284 subentryAttrs.put( operational );
285 }
286 }
287 else if ( role.equalsIgnoreCase( AC_AREA ) || role.equalsIgnoreCase( AC_INNERAREA ) )
288 {
289 operational = subentryAttrs.get( AC_SUBENTRY );
290 if ( operational == null )
291 {
292 operational = new LockableAttributeImpl( AC_SUBENTRY );
293 subentryAttrs.put( operational );
294 }
295 }
296 else if ( role.equalsIgnoreCase( SCHEMA_AREA ) )
297 {
298 operational = subentryAttrs.get( SCHEMA_AREA_SUBENTRY );
299 if ( operational == null )
300 {
301 operational = new LockableAttributeImpl( SCHEMA_AREA_SUBENTRY );
302 subentryAttrs.put( operational );
303 }
304 }
305 else if ( role.equalsIgnoreCase( COLLECTIVE_AREA ) ||
306 role.equalsIgnoreCase( COLLECTIVE_INNERAREA ) )
307 {
308 operational = subentryAttrs.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES );
309 if ( operational == null )
310 {
311 operational = new LockableAttributeImpl( COLLECTIVE_ATTRIBUTE_SUBENTRIES );
312 subentryAttrs.put( operational );
313 }
314 }
315 else
316 {
317 throw new LdapInvalidAttributeValueException( "Encountered invalid administrativeRole '"
318 + role + "' in administrative point of subentry " + subentryDnStr + ". The values of this attribute"
319 + " are constrained to autonomousArea, accessControlSpecificArea, accessControlInnerArea,"
320 + " subschemaAdminSpecificArea, collectiveAttributeSpecificArea, and"
321 + " collectiveAttributeInnerArea.", ResultCodeEnum.CONSTRAINTVIOLATION );
322 }
323
324 operational.add( subentryDn.toString() );
325 }
326 }
327 }
328
329 return subentryAttrs;
330 }
331
332
333 public void add( NextInterceptor next, String upName, Name normName, Attributes entry ) throws NamingException
334 {
335 Attribute objectClasses = entry.get( "objectClass" );
336
337 if ( objectClasses.contains( "subentry" ) )
338 {
339
340 Name apName = ( Name ) normName.clone();
341 apName.remove( normName.size() - 1 );
342 Attributes ap = nexus.lookup( apName );
343 Attribute administrativeRole = ap.get( "administrativeRole" );
344
345
346 if ( administrativeRole == null || administrativeRole.size() <= 0 )
347 {
348 throw new LdapNoSuchAttributeException( "Administration point " + apName
349 + " does not contain an administrativeRole attribute! An"
350 + " administrativeRole attribute in the administrative point is"
351 + " required to add a subordinate subentry." );
352 }
353
354
355
356
357
358
359
360
361
362 Attributes operational = getSubentryOperatationalAttributes( normName, administrativeRole );
363
364
365
366
367
368
369
370
371 String subtree = ( String ) entry.get( "subtreeSpecification" ).get();
372 SubtreeSpecification ss;
373 try
374 {
375 ss = ssParser.parse( subtree );
376 }
377 catch ( Exception e )
378 {
379 String msg = "Failed while parsing subtreeSpecification for " + upName;
380 log.warn( msg );
381 throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALIDATTRIBUTESYNTAX );
382 }
383 subtrees.put( normName.toString(), ss );
384 next.add( upName, normName, entry );
385
386
387
388
389
390
391
392
393
394 Name baseDn = ( Name ) apName.clone();
395 baseDn.addAll( ss.getBase() );
396
397 ExprNode filter = new PresenceNode( "objectclass" );
398 SearchControls controls = new SearchControls();
399 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
400 controls.setReturningAttributes( new String[] { "+", "*" } );
401
402 NamingEnumeration subentries = nexus.search( baseDn, factoryCfg.getEnvironment(), filter, controls );
403 while ( subentries.hasMore() )
404 {
405 SearchResult result = ( SearchResult ) subentries.next();
406 Attributes candidate = result.getAttributes();
407 Name dn = dnParser.parse( result.getName() );
408
409 if ( evaluator.evaluate( ss, apName, dn, candidate.get( "objectClass" ) ) )
410 {
411 nexus.modify( dn, getOperationalModsForAdd( candidate, operational ) );
412 }
413 }
414 }
415 else
416 {
417 Iterator list = subtrees.keySet().iterator();
418 while ( list.hasNext() )
419 {
420 String subentryDnStr = ( String ) list.next();
421 Name subentryDn = new LdapName( subentryDnStr );
422 Name apDn = ( Name ) subentryDn.clone();
423 apDn.remove( apDn.size() - 1 );
424 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( subentryDn );
425
426 if ( evaluator.evaluate( ss, apDn, normName, objectClasses ) )
427 {
428 Attribute administrativeRole = nexus.lookup( apDn ).get( "administrativeRole" );
429 NamingEnumeration roles = administrativeRole.getAll();
430 while ( roles.hasMore() )
431 {
432 Attribute operational;
433 String role = ( String ) roles.next();
434
435 if ( role.equalsIgnoreCase( AUTONOUMOUS_AREA ) )
436 {
437 operational = entry.get( AUTONOUMOUS_AREA_SUBENTRY );
438 if ( operational == null )
439 {
440 operational = new LockableAttributeImpl( AUTONOUMOUS_AREA_SUBENTRY );
441 entry.put( operational );
442 }
443 }
444 else if ( role.equalsIgnoreCase( AC_AREA ) || role.equalsIgnoreCase( AC_INNERAREA ) )
445 {
446 operational = ( Attribute ) entry.get( AC_SUBENTRY );
447 if ( operational == null )
448 {
449 operational = new LockableAttributeImpl( AC_SUBENTRY );
450 entry.put( operational );
451 }
452 }
453 else if ( role.equalsIgnoreCase( SCHEMA_AREA ) )
454 {
455 operational = ( Attribute ) entry.get( SCHEMA_AREA_SUBENTRY );
456 if ( operational == null )
457 {
458 operational = new LockableAttributeImpl( SCHEMA_AREA_SUBENTRY );
459 entry.put( operational );
460 }
461 }
462 else if ( role.equalsIgnoreCase( COLLECTIVE_AREA ) ||
463 role.equalsIgnoreCase( COLLECTIVE_INNERAREA ) )
464 {
465 operational = ( Attribute ) entry.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES );
466 if ( operational == null )
467 {
468 operational = new LockableAttributeImpl( COLLECTIVE_ATTRIBUTE_SUBENTRIES );
469 entry.put( operational );
470 }
471 }
472 else
473 {
474 throw new LdapInvalidAttributeValueException( "Encountered invalid administrativeRole '"
475 + role + "' in administrative point of subentry " + subentryDnStr + ". The values of this attribute"
476 + " are constrained to autonomousArea, accessControlSpecificArea, accessControlInnerArea,"
477 + " subschemaAdminSpecificArea, collectiveAttributeSpecificArea, and"
478 + " collectiveAttributeInnerArea.", ResultCodeEnum.CONSTRAINTVIOLATION );
479 }
480
481 operational.add( subentryDn.toString() );
482 }
483 }
484 }
485
486 next.add( upName, normName, entry );
487 }
488 }
489
490
491
492
493
494
495
496 public void delete( NextInterceptor next, Name name ) throws NamingException
497 {
498 Attributes entry = nexus.lookup( name );
499 Attribute objectClasses = entry.get( "objectClass" );
500
501 if ( objectClasses.contains( "subentry" ) )
502 {
503 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( name.toString() );
504 subtrees.remove( ss );
505 next.delete( name );
506
507
508
509
510
511
512
513
514
515 Name apName = ( Name ) name.clone();
516 apName.remove( name.size() - 1 );
517 Name baseDn = ( Name ) apName.clone();
518 baseDn.addAll( ss.getBase() );
519
520 ExprNode filter = new PresenceNode( "objectclass" );
521 SearchControls controls = new SearchControls();
522 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
523 controls.setReturningAttributes( new String[] { "+", "*" } );
524
525 NamingEnumeration subentries = nexus.search( baseDn, factoryCfg.getEnvironment(), filter, controls );
526 while ( subentries.hasMore() )
527 {
528 SearchResult result = ( SearchResult ) subentries.next();
529 Attributes candidate = result.getAttributes();
530 Name dn = dnParser.parse( result.getName() );
531
532 if ( evaluator.evaluate( ss, apName, dn, candidate.get( "objectClass" ) ) )
533 {
534 nexus.modify( dn, getOperationalModsForRemove( name, candidate ) );
535 }
536 }
537 }
538 else
539 {
540 next.delete( name );
541 }
542 }
543
544
545
546
547
548
549
550 /***
551 * Checks to see if an entry being renamed has a descendant that is an
552 * administrative point.
553 *
554 * @param name the name of the entry which is used as the search base
555 * @return true if name is an administrative point or one of its descendants
556 * are, false otherwise
557 * @throws NamingException if there are errors while searching the directory
558 */
559 private boolean hasAdministrativeDescendant( Name name ) throws NamingException
560 {
561 ExprNode filter = new PresenceNode( "administrativeRole" );
562 SearchControls controls = new SearchControls();
563 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
564 NamingEnumeration aps = nexus.search( name, factoryCfg.getEnvironment(), filter, controls );
565 if ( aps.hasMore() )
566 {
567 aps.close();
568 return true;
569 }
570
571 return false;
572 }
573
574
575 private ModificationItem[] getModsOnEntryRdnChange( Name oldName, Name newName, Attributes entry )
576 throws NamingException
577 {
578 Attribute objectClasses = entry.get( "objectClass" );
579 List modList = new ArrayList();
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594 Iterator subentries = subtrees.keySet().iterator();
595 while ( subentries.hasNext() )
596 {
597 String subentryDn = ( String ) subentries.next();
598 Name apDn = new LdapName( subentryDn );
599 apDn.remove( apDn.size() - 1 );
600 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( subentryDn );
601 boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, objectClasses );
602 boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, objectClasses );
603
604 if ( isOldNameSelected == isNewNameSelected )
605 {
606 continue;
607 }
608
609
610 if ( isOldNameSelected && ! isNewNameSelected )
611 {
612 for ( int ii = 0; ii < SUBENTRY_OPATTRS.length; ii++ )
613 {
614 int op = DirContext.REPLACE_ATTRIBUTE;
615 Attribute opAttr = entry.get( SUBENTRY_OPATTRS[ii] );
616 if ( opAttr != null )
617 {
618 opAttr = ( Attribute ) opAttr.clone();
619 opAttr.remove( subentryDn );
620
621 if ( opAttr.size() < 1 )
622 {
623 op = DirContext.REMOVE_ATTRIBUTE;
624 }
625
626 modList.add( new ModificationItem( op, opAttr ) );
627 }
628 }
629 }
630
631 else if ( isNewNameSelected && ! isOldNameSelected )
632 {
633 for ( int ii = 0; ii < SUBENTRY_OPATTRS.length; ii++ )
634 {
635 int op = DirContext.REPLACE_ATTRIBUTE;
636 Attribute opAttr = entry.get( SUBENTRY_OPATTRS[ii] );
637 if ( opAttr != null )
638 {
639 opAttr = ( Attribute ) opAttr.clone();
640 }
641 else
642 {
643 op = DirContext.ADD_ATTRIBUTE;
644 opAttr = new LockableAttributeImpl( SUBENTRY_OPATTRS[ii] );
645 }
646
647 opAttr.add( subentryDn );
648 modList.add( new ModificationItem( op, opAttr ) );
649 }
650 }
651 }
652
653 ModificationItem[] mods = new ModificationItem[modList.size()];
654 mods = ( ModificationItem[] ) modList.toArray( mods );
655 return mods;
656 }
657
658
659 public void modifyRn( NextInterceptor next, Name name, String newRn, boolean deleteOldRn ) throws NamingException
660 {
661 Attributes entry = nexus.lookup( name );
662 Attribute objectClasses = entry.get( "objectClass" );
663
664 if ( objectClasses.contains( "subentry" ) )
665 {
666 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( name.toString() );
667 Name apName = ( Name ) name.clone();
668 apName.remove( apName.size() - 1 );
669 Name baseDn = ( Name ) apName.clone();
670 baseDn.addAll( ss.getBase() );
671 Name newName = ( Name ) name.clone();
672 newName.remove( newName.size() - 1 );
673 Name rdn = dnParser.parse( newRn );
674 newName.addAll( rdn );
675
676 subtrees.put( newName.toString(), ss );
677 next.modifyRn( name, newRn, deleteOldRn );
678
679 Attributes apAttrs = nexus.lookup( apName );
680 Attribute administrativeRole = apAttrs.get( "administrativeRole" );
681 ExprNode filter = new PresenceNode( "objectclass" );
682 SearchControls controls = new SearchControls();
683 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
684 controls.setReturningAttributes( new String[] { "+", "*" } );
685 NamingEnumeration subentries = nexus.search( baseDn, factoryCfg.getEnvironment(), filter, controls );
686 while ( subentries.hasMore() )
687 {
688 SearchResult result = ( SearchResult ) subentries.next();
689 Attributes candidate = result.getAttributes();
690 Name dn = dnParser.parse( result.getName() );
691
692 if ( evaluator.evaluate( ss, apName, dn, candidate.get( "objectClass" ) ) )
693 {
694 nexus.modify( dn, getOperationalModsForReplace( name, newName, administrativeRole, candidate ) );
695 }
696 }
697 }
698 else
699 {
700 if ( hasAdministrativeDescendant( name ) )
701 {
702 String msg = "Will not allow rename operation on entries with administrative descendants.";
703 log.warn( msg );
704 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
705 }
706 next.modifyRn( name, newRn, deleteOldRn );
707
708
709
710 Name newName = ( Name ) name.clone();
711 newName.remove( newName.size() - 1 );
712 newName.add( newRn );
713 ModificationItem[] mods = getModsOnEntryRdnChange( name, newName, entry );
714
715 if ( mods.length > 0 )
716 {
717 nexus.modify( newName, mods );
718 }
719 }
720 }
721
722
723 public void move( NextInterceptor next, Name oriChildName, Name newParentName, String newRn, boolean deleteOldRn )
724 throws NamingException
725 {
726 Attributes entry = nexus.lookup( oriChildName );
727 Attribute objectClasses = entry.get( "objectClass" );
728
729 if ( objectClasses.contains( "subentry" ) )
730 {
731 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( oriChildName.toString() );
732 Name apName = ( Name ) oriChildName.clone();
733 apName.remove( apName.size() - 1 );
734 Name baseDn = ( Name ) apName.clone();
735 baseDn.addAll( ss.getBase() );
736 Name newName = ( Name ) newParentName.clone();
737 newName.remove( newName.size() - 1 );
738 Name rdn = dnParser.parse( newRn );
739 newName.addAll( rdn );
740
741 subtrees.put( newName.toString(), ss );
742 next.move( oriChildName, newParentName, newRn, deleteOldRn );
743
744 Attributes apAttrs = nexus.lookup( apName );
745 Attribute administrativeRole = apAttrs.get( "administrativeRole" );
746
747 ExprNode filter = new PresenceNode( "objectclass" );
748 SearchControls controls = new SearchControls();
749 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
750 controls.setReturningAttributes( new String[] { "+", "*" } );
751 NamingEnumeration subentries = nexus.search( baseDn, factoryCfg.getEnvironment(), filter, controls );
752 while ( subentries.hasMore() )
753 {
754 SearchResult result = ( SearchResult ) subentries.next();
755 Attributes candidate = result.getAttributes();
756 Name dn = dnParser.parse( result.getName() );
757
758 if ( evaluator.evaluate( ss, apName, dn, candidate.get( "objectClass" ) ) )
759 {
760 nexus.modify( dn, getOperationalModsForReplace( oriChildName, newName, administrativeRole, candidate ) );
761 }
762 }
763 }
764 else
765 {
766 if ( hasAdministrativeDescendant( oriChildName ) )
767 {
768 String msg = "Will not allow rename operation on entries with administrative descendants.";
769 log.warn( msg );
770 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
771 }
772 next.move( oriChildName, newParentName, newRn, deleteOldRn );
773
774
775
776 Name newName = ( Name ) newParentName.clone();
777 newName.add( newRn );
778 ModificationItem[] mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
779
780 if ( mods.length > 0 )
781 {
782 nexus.modify( newName, mods );
783 }
784 }
785 }
786
787
788 public void move( NextInterceptor next, Name oriChildName, Name newParentName ) throws NamingException
789 {
790 Attributes entry = nexus.lookup( oriChildName );
791 Attribute objectClasses = entry.get( "objectClass" );
792
793 if ( objectClasses.contains( "subentry" ) )
794 {
795 SubtreeSpecification ss = ( SubtreeSpecification ) subtrees.get( oriChildName.toString() );
796 Name apName = ( Name ) oriChildName.clone();
797 apName.remove( apName.size() - 1 );
798 Name baseDn = ( Name ) apName.clone();
799 baseDn.addAll( ss.getBase() );
800 Name newName = ( Name ) newParentName.clone();
801 newName.remove( newName.size() - 1 );
802 newName.add( newParentName.get( newParentName.size() - 1 ) );
803
804 subtrees.put( newName.toString(), ss );
805 next.move( oriChildName, newParentName );
806
807 Attributes apAttrs = nexus.lookup( apName );
808 Attribute administrativeRole = apAttrs.get( "administrativeRole" );
809
810 ExprNode filter = new PresenceNode( "objectclass" );
811 SearchControls controls = new SearchControls();
812 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
813 controls.setReturningAttributes( new String[] { "+", "*" } );
814 NamingEnumeration subentries = nexus.search( baseDn, factoryCfg.getEnvironment(), filter, controls );
815 while ( subentries.hasMore() )
816 {
817 SearchResult result = ( SearchResult ) subentries.next();
818 Attributes candidate = result.getAttributes();
819 Name dn = dnParser.parse( result.getName() );
820
821 if ( evaluator.evaluate( ss, apName, dn, candidate.get( "objectClass" ) ) )
822 {
823 nexus.modify( dn, getOperationalModsForReplace( oriChildName, newName, administrativeRole, candidate ) );
824 }
825 }
826 }
827 else
828 {
829 if ( hasAdministrativeDescendant( oriChildName ) )
830 {
831 String msg = "Will not allow rename operation on entries with administrative descendants.";
832 log.warn( msg );
833 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
834 }
835 next.move( oriChildName, newParentName );
836
837
838
839 Name newName = ( Name ) newParentName.clone();
840 newName.add( oriChildName.get( oriChildName.size() - 1 ) );
841 ModificationItem[] mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
842
843 if ( mods.length > 0 )
844 {
845 nexus.modify( newName, mods );
846 }
847 }
848 }
849
850
851
852
853
854
855
856 public void modify( NextInterceptor next, Name name, int modOp, Attributes mods ) throws NamingException
857 {
858 Attributes entry = nexus.lookup( name );
859 Attribute objectClasses = entry.get( "objectClass" );
860
861 if ( objectClasses.contains( "subentry" ) && mods.get( "subtreeSpecification" ) != null )
862 {
863 SubtreeSpecification ssOld = ( SubtreeSpecification ) subtrees.remove( name.toString() );
864 SubtreeSpecification ssNew;
865
866 try
867 {
868 ssNew = ssParser.parse( ( String ) mods.get( "subtreeSpecification" ).get() );
869 }
870 catch ( Exception e )
871 {
872 String msg = "failed to parse the new subtreeSpecification";
873 log.error( msg, e );
874 throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALIDATTRIBUTESYNTAX );
875 }
876
877 subtrees.put( name.toString(), ssNew );
878 next.modify( name, modOp, mods );
879
880
881 Name apName = ( Name ) name.clone();
882 apName.remove( apName.size() - 1 );
883 Name oldBaseDn = ( Name ) apName.clone();
884 oldBaseDn.addAll( ssOld.getBase() );
885 ExprNode filter = new PresenceNode( "objectClass" );
886 SearchControls controls = new SearchControls();
887 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
888 controls.setReturningAttributes( new String[] { "+", "*" } );
889 NamingEnumeration subentries = nexus.search( oldBaseDn, factoryCfg.getEnvironment(), filter, controls );
890 while ( subentries.hasMore() )
891 {
892 SearchResult result = ( SearchResult ) subentries.next();
893 Attributes candidate = result.getAttributes();
894 Name dn = dnParser.parse( result.getName() );
895
896 if ( evaluator.evaluate( ssOld, apName, dn, candidate.get( "objectClass" ) ) )
897 {
898 nexus.modify( dn, getOperationalModsForRemove( name, candidate ) );
899 }
900 }
901
902
903 Attributes apAttrs = nexus.lookup( apName );
904 Attribute administrativeRole = apAttrs.get( "administrativeRole" );
905 Attributes operational = getSubentryOperatationalAttributes( name, administrativeRole );
906 Name newBaseDn = ( Name ) apName.clone();
907 newBaseDn.addAll( ssNew.getBase() );
908 subentries = nexus.search( newBaseDn, factoryCfg.getEnvironment(), filter, controls );
909 while ( subentries.hasMore() )
910 {
911 SearchResult result = ( SearchResult ) subentries.next();
912 Attributes candidate = result.getAttributes();
913 Name dn = dnParser.parse( result.getName() );
914
915 if ( evaluator.evaluate( ssNew, apName, dn, candidate.get( "objectClass" ) ) )
916 {
917 nexus.modify( dn, getOperationalModsForAdd( candidate, operational ) );
918 }
919 }
920 }
921 else
922 {
923 next.modify( name, modOp, mods );
924 }
925 }
926
927
928 public void modify( NextInterceptor next, Name name, ModificationItem[] mods ) throws NamingException
929 {
930 Attributes entry = nexus.lookup( name );
931 Attribute objectClasses = entry.get( "objectClass" );
932 boolean isSubtreeSpecificationModification = false;
933 ModificationItem subtreeMod = null;
934
935 for ( int ii = 0; ii < mods.length; ii++ )
936 {
937 if ( "subtreeSpecification".equalsIgnoreCase( mods[ii].getAttribute().getID() ) )
938 {
939 isSubtreeSpecificationModification = true;
940 subtreeMod = mods[ii];
941 }
942 }
943
944 if ( objectClasses.contains( "subentry" ) && isSubtreeSpecificationModification )
945 {
946 SubtreeSpecification ssOld = ( SubtreeSpecification ) subtrees.remove( name.toString() );
947 SubtreeSpecification ssNew;
948
949 try
950 {
951 ssNew = ssParser.parse( ( String ) subtreeMod.getAttribute().get() );
952 }
953 catch ( Exception e )
954 {
955 String msg = "failed to parse the new subtreeSpecification";
956 log.error( msg, e );
957 throw new LdapInvalidAttributeValueException( msg, ResultCodeEnum.INVALIDATTRIBUTESYNTAX );
958 }
959
960 subtrees.put( name.toString(), ssNew );
961 next.modify( name, mods );
962
963
964 Name apName = ( Name ) name.clone();
965 apName.remove( apName.size() - 1 );
966 Name oldBaseDn = ( Name ) apName.clone();
967 oldBaseDn.addAll( ssOld.getBase() );
968 ExprNode filter = new PresenceNode( "objectClass" );
969 SearchControls controls = new SearchControls();
970 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
971 controls.setReturningAttributes( new String[] { "+", "*" } );
972 NamingEnumeration subentries = nexus.search( oldBaseDn, factoryCfg.getEnvironment(), filter, controls );
973 while ( subentries.hasMore() )
974 {
975 SearchResult result = ( SearchResult ) subentries.next();
976 Attributes candidate = result.getAttributes();
977 Name dn = dnParser.parse( result.getName() );
978
979 if ( evaluator.evaluate( ssOld, apName, dn, candidate.get( "objectClass" ) ) )
980 {
981 nexus.modify( dn, getOperationalModsForRemove( name, candidate ) );
982 }
983 }
984
985
986 Attributes apAttrs = nexus.lookup( apName );
987 Attribute administrativeRole = apAttrs.get( "administrativeRole" );
988 Attributes operational = getSubentryOperatationalAttributes( name, administrativeRole );
989 Name newBaseDn = ( Name ) apName.clone();
990 newBaseDn.addAll( ssNew.getBase() );
991 subentries = nexus.search( newBaseDn, factoryCfg.getEnvironment(), filter, controls );
992 while ( subentries.hasMore() )
993 {
994 SearchResult result = ( SearchResult ) subentries.next();
995 Attributes candidate = result.getAttributes();
996 Name dn = dnParser.parse( result.getName() );
997
998 if ( evaluator.evaluate( ssNew, apName, dn, candidate.get( "objectClass" ) ) )
999 {
1000 nexus.modify( dn, getOperationalModsForAdd( candidate, operational ) );
1001 }
1002 }
1003 }
1004 else
1005 {
1006 next.modify( name, mods );
1007 }
1008 }
1009
1010
1011
1012
1013
1014
1015
1016 private ModificationItem[] getOperationalModsForReplace( Name oldName, Name newName, Attribute administrativeRole,
1017 Attributes entry ) throws NamingException
1018 {
1019 List modList = new ArrayList();
1020 NamingEnumeration roles = administrativeRole.getAll();
1021 while ( roles.hasMore() )
1022 {
1023 Attribute operational;
1024 String role = ( String ) roles.next();
1025
1026 if ( role.equalsIgnoreCase( AUTONOUMOUS_AREA ) )
1027 {
1028 operational = ( Attribute ) entry.get( AUTONOUMOUS_AREA_SUBENTRY ).clone();
1029 if ( operational == null )
1030 {
1031 operational = new LockableAttributeImpl( AUTONOUMOUS_AREA_SUBENTRY );
1032 operational.add( newName.toString() );
1033 }
1034 else
1035 {
1036 operational.remove( oldName.toString() );
1037 operational.add( newName.toString() );
1038 }
1039 }
1040 else if ( role.equalsIgnoreCase( AC_AREA ) || role.equalsIgnoreCase( AC_INNERAREA ) )
1041 {
1042 operational = ( Attribute ) entry.get( AC_SUBENTRY ).clone();
1043 if ( operational == null )
1044 {
1045 operational = new LockableAttributeImpl( AC_SUBENTRY );
1046 operational.add( newName.toString() );
1047 }
1048 else
1049 {
1050 operational.remove( oldName.toString() );
1051 operational.add( newName.toString() );
1052 }
1053 }
1054 else if ( role.equalsIgnoreCase( SCHEMA_AREA ) )
1055 {
1056 operational = ( Attribute ) entry.get( SCHEMA_AREA_SUBENTRY ).clone();
1057 if ( operational == null )
1058 {
1059 operational = new LockableAttributeImpl( SCHEMA_AREA_SUBENTRY );
1060 operational.add( newName.toString() );
1061 }
1062 else
1063 {
1064 operational.remove( oldName.toString() );
1065 operational.add( newName.toString() );
1066 }
1067 }
1068 else if ( role.equalsIgnoreCase( COLLECTIVE_AREA ) ||
1069 role.equalsIgnoreCase( COLLECTIVE_INNERAREA ) )
1070 {
1071 operational = ( Attribute ) entry.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES ).clone();
1072 if ( operational == null )
1073 {
1074 operational = new LockableAttributeImpl( COLLECTIVE_ATTRIBUTE_SUBENTRIES );
1075 operational.add( newName.toString() );
1076 }
1077 else
1078 {
1079 operational.remove( oldName.toString() );
1080 operational.add( newName.toString() );
1081 }
1082 }
1083 else
1084 {
1085 throw new LdapInvalidAttributeValueException( "Encountered invalid administrativeRole '"
1086 + role + "' in administrative point of subentry " + oldName + ". The values of this attribute"
1087 + " are constrained to autonomousArea, accessControlSpecificArea, accessControlInnerArea,"
1088 + " subschemaAdminSpecificArea, collectiveAttributeSpecificArea, and"
1089 + " collectiveAttributeInnerArea.", ResultCodeEnum.CONSTRAINTVIOLATION );
1090 }
1091
1092 modList.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, operational ) );
1093 }
1094
1095 ModificationItem[] mods = new ModificationItem[modList.size()];
1096 return ( ModificationItem[] ) modList.toArray( mods );
1097 }
1098
1099
1100 /***
1101 * Gets the subschema operational attributes to be added to or removed from
1102 * an entry selected by a subentry's subtreeSpecification.
1103 *
1104 * @param name the normalized distinguished name of the subentry (the value of op attrs)
1105 * @param administrativeRole the roles the administrative point participates in
1106 * @return the set of attributes to be added or removed from entries
1107 * @throws NamingException if there are problems accessing attributes
1108 */
1109 private Attributes getSubentryOperatationalAttributes( Name name, Attribute administrativeRole )
1110 throws NamingException
1111 {
1112 Attributes operational = new LockableAttributesImpl();
1113 NamingEnumeration roles = administrativeRole.getAll();
1114 while ( roles.hasMore() )
1115 {
1116 String role = ( String ) roles.next();
1117
1118 if ( role.equalsIgnoreCase( AUTONOUMOUS_AREA ) )
1119 {
1120 if ( operational.get( AUTONOUMOUS_AREA_SUBENTRY ) == null )
1121 {
1122 operational.put( AUTONOUMOUS_AREA_SUBENTRY, name.toString() );
1123 }
1124 else
1125 {
1126 operational.get( AUTONOUMOUS_AREA_SUBENTRY ).add( name.toString() );
1127 }
1128 }
1129 else if ( role.equalsIgnoreCase( AC_AREA ) || role.equalsIgnoreCase( AC_INNERAREA ) )
1130 {
1131 if ( operational.get( AC_SUBENTRY ) == null )
1132 {
1133 operational.put( AC_SUBENTRY, name.toString() );
1134 }
1135 else
1136 {
1137 operational.get( AC_SUBENTRY ).add( name.toString() );
1138 }
1139 }
1140 else if ( role.equalsIgnoreCase( SCHEMA_AREA ) )
1141 {
1142 if ( operational.get( SCHEMA_AREA_SUBENTRY ) == null )
1143 {
1144 operational.put( SCHEMA_AREA_SUBENTRY, name.toString() );
1145 }
1146 else
1147 {
1148 operational.get( SCHEMA_AREA_SUBENTRY ).add( name.toString() );
1149 }
1150 }
1151 else if ( role.equalsIgnoreCase( COLLECTIVE_AREA ) ||
1152 role.equalsIgnoreCase( COLLECTIVE_INNERAREA ) )
1153 {
1154 if ( operational.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES ) == null )
1155 {
1156 operational.put( COLLECTIVE_ATTRIBUTE_SUBENTRIES, name.toString() );
1157 }
1158 else
1159 {
1160 operational.get( COLLECTIVE_ATTRIBUTE_SUBENTRIES ).add( name.toString() );
1161 }
1162 }
1163 else
1164 {
1165 throw new LdapInvalidAttributeValueException( "Encountered invalid administrativeRole '"
1166 + role + "' in administrative point of subentry " + name + ". The values of this attribute are"
1167 + " constrained to autonomousArea, accessControlSpecificArea, accessControlInnerArea,"
1168 + " subschemaAdminSpecificArea, collectiveAttributeSpecificArea, and"
1169 + " collectiveAttributeInnerArea.", ResultCodeEnum.CONSTRAINTVIOLATION );
1170 }
1171 }
1172
1173 return operational;
1174 }
1175
1176
1177 /***
1178 * Calculates the subentry operational attributes to remove from a candidate
1179 * entry selected by a subtreeSpecification. When we remove a subentry we
1180 * must remove the operational attributes in the entries that were once selected
1181 * by the subtree specification of that subentry. To do so we must perform
1182 * a modify operation with the set of modifications to perform. This method
1183 * calculates those modifications.
1184 *
1185 * @param subentryDn the distinguished name of the subentry
1186 * @param candidate the candidate entry to removed from the
1187 * @return the set of modifications required to remove an entry's reference to
1188 * a subentry
1189 */
1190 private ModificationItem[] getOperationalModsForRemove( Name subentryDn, Attributes candidate )
1191 {
1192 List modList = new ArrayList();
1193 String dn = subentryDn.toString();
1194
1195 for ( int ii = 0; ii < SUBENTRY_OPATTRS.length; ii++ )
1196 {
1197 String opAttrId = SUBENTRY_OPATTRS[ii];
1198 Attribute opAttr = candidate.get( opAttrId );
1199
1200 if ( opAttr != null && opAttr.contains( dn ) )
1201 {
1202 Attribute attr = new LockableAttributeImpl( SUBENTRY_OPATTRS[ii] );
1203 attr.add( dn );
1204 modList.add( new ModificationItem( DirContext.REMOVE_ATTRIBUTE, attr ) );
1205 }
1206 }
1207
1208 ModificationItem[] mods = new ModificationItem[modList.size()];
1209 return ( ModificationItem[] ) modList.toArray( mods );
1210 }
1211
1212
1213 /***
1214 * Calculates the subentry operational attributes to add or replace from
1215 * a candidate entry selected by a subtree specification. When a subentry
1216 * is added or it's specification is modified some entries must have new
1217 * operational attributes added to it to point back to the associated
1218 * subentry. To do so a modify operation must be performed on entries
1219 * selected by the subtree specification. This method calculates the
1220 * modify operation to be performed on the entry.
1221 *
1222 * @param entry the entry being modified
1223 * @param operational the set of operational attributes supported by the AP
1224 * of the subentry
1225 * @return the set of modifications needed to update the entry
1226 */
1227 public ModificationItem[] getOperationalModsForAdd( Attributes entry, Attributes operational ) throws NamingException
1228 {
1229 List modList = new ArrayList();
1230
1231 NamingEnumeration opAttrIds = operational.getIDs();
1232 while ( opAttrIds.hasMore() )
1233 {
1234 int op = DirContext.REPLACE_ATTRIBUTE;
1235 String opAttrId = ( String ) opAttrIds.next();
1236 Attribute result = new LockableAttributeImpl( opAttrId );
1237 Attribute opAttrAdditions = operational.get( opAttrId );
1238 Attribute opAttrInEntry = entry.get( opAttrId );
1239
1240 for ( int ii = 0; ii < opAttrAdditions.size(); ii++ )
1241 {
1242 result.add( opAttrAdditions.get( ii ) );
1243 }
1244
1245 if ( opAttrInEntry != null && opAttrInEntry.size() > 0 )
1246 {
1247 for ( int ii = 0; ii < opAttrInEntry.size(); ii++ )
1248 {
1249 result.add( opAttrInEntry.get( ii ) );
1250 }
1251 }
1252 else
1253 {
1254 op = DirContext.ADD_ATTRIBUTE;
1255 }
1256
1257 modList.add( new ModificationItem( op, result ) );
1258 }
1259
1260 ModificationItem[] mods = new ModificationItem[modList.size()];
1261 mods = ( ModificationItem[] ) modList.toArray( mods );
1262 return mods;
1263 }
1264 }