001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.core.subtree;
021
022
023 import java.util.ArrayList;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Set;
028
029 import javax.naming.directory.SearchControls;
030
031 import org.apache.directory.server.constants.ApacheSchemaConstants;
032 import org.apache.directory.server.constants.ServerDNConstants;
033 import org.apache.directory.server.core.CoreSession;
034 import org.apache.directory.server.core.DefaultCoreSession;
035 import org.apache.directory.server.core.DirectoryService;
036 import org.apache.directory.server.core.LdapPrincipal;
037 import org.apache.directory.server.core.entry.ClonedServerEntry;
038 import org.apache.directory.server.core.filtering.EntryFilter;
039 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
040 import org.apache.directory.server.core.interceptor.BaseInterceptor;
041 import org.apache.directory.server.core.interceptor.NextInterceptor;
042 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
043 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
044 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
045 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
046 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
047 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
048 import org.apache.directory.server.core.interceptor.context.OperationContext;
049 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
050 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
051 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
052 import org.apache.directory.server.core.partition.ByPassConstants;
053 import org.apache.directory.server.core.partition.PartitionNexus;
054 import org.apache.directory.server.i18n.I18n;
055 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
056 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
057 import org.apache.directory.shared.ldap.constants.SchemaConstants;
058 import org.apache.directory.shared.ldap.entry.StringValue;
059 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
060 import org.apache.directory.shared.ldap.entry.DefaultServerEntry;
061 import org.apache.directory.shared.ldap.entry.EntryAttribute;
062 import org.apache.directory.shared.ldap.entry.Modification;
063 import org.apache.directory.shared.ldap.entry.ModificationOperation;
064 import org.apache.directory.shared.ldap.entry.ServerEntry;
065 import org.apache.directory.shared.ldap.entry.ServerModification;
066 import org.apache.directory.shared.ldap.entry.Value;
067 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeValueException;
068 import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
069 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
070 import org.apache.directory.shared.ldap.filter.EqualityNode;
071 import org.apache.directory.shared.ldap.filter.ExprNode;
072 import org.apache.directory.shared.ldap.filter.PresenceNode;
073 import org.apache.directory.shared.ldap.filter.SearchScope;
074 import org.apache.directory.shared.ldap.message.AliasDerefMode;
075 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
076 import org.apache.directory.shared.ldap.name.DN;
077 import org.apache.directory.shared.ldap.schema.AttributeType;
078 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
079 import org.apache.directory.shared.ldap.schema.SchemaManager;
080 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer;
081 import org.apache.directory.shared.ldap.schema.registries.OidRegistry;
082 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
083 import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationParser;
084 import org.slf4j.Logger;
085 import org.slf4j.LoggerFactory;
086
087
088 /**
089 * The Subentry interceptor service which is responsible for filtering
090 * out subentries on search operations and injecting operational attributes
091 *
092 * @org.apache.xbean.XBean
093 *
094 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
095 * @version $Rev: 928945 $
096 */
097 public class SubentryInterceptor extends BaseInterceptor
098 {
099 /** the subentry control OID */
100 private static final String SUBENTRY_CONTROL = SubentriesControl.CONTROL_OID;
101
102 public static final String AC_AREA = "accessControlSpecificArea";
103 public static final String AC_INNERAREA = "accessControlInnerArea";
104
105 public static final String SCHEMA_AREA = "subschemaAdminSpecificArea";
106
107 public static final String COLLECTIVE_AREA = "collectiveAttributeSpecificArea";
108 public static final String COLLECTIVE_INNERAREA = "collectiveAttributeInnerArea";
109
110 public static final String TRIGGER_AREA = "triggerExecutionSpecificArea";
111 public static final String TRIGGER_INNERAREA = "triggerExecutionInnerArea";
112
113 public static final String[] SUBENTRY_OPATTRS =
114 { SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, SchemaConstants.SUBSCHEMA_SUBENTRY_AT,
115 SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT };
116
117 private static final Logger LOG = LoggerFactory.getLogger( SubentryInterceptor.class );
118
119 /** the hash mapping the DN of a subentry to its SubtreeSpecification/types */
120 private final SubentryCache subentryCache = new SubentryCache();
121
122 private SubtreeSpecificationParser ssParser;
123 private SubtreeEvaluator evaluator;
124 private PartitionNexus nexus;
125
126 /** The global registries */
127 private SchemaManager schemaManager;
128
129 /** The OID registry */
130 private OidRegistry oidRegistry;
131
132 private AttributeType objectClassType;
133
134
135 public void init( DirectoryService directoryService ) throws Exception
136 {
137 super.init( directoryService );
138 nexus = directoryService.getPartitionNexus();
139 schemaManager = directoryService.getSchemaManager();
140 oidRegistry = schemaManager.getGlobalOidRegistry();
141
142 // setup various attribute type values
143 objectClassType = schemaManager.lookupAttributeTypeRegistry( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) );
144
145 ssParser = new SubtreeSpecificationParser( new NormalizerMappingResolver()
146 {
147 public Map<String, OidNormalizer> getNormalizerMapping() throws Exception
148 {
149 return schemaManager.getNormalizerMapping();
150 }
151 }, schemaManager.getNormalizerMapping() );
152 evaluator = new SubtreeEvaluator( oidRegistry, schemaManager );
153
154 // prepare to find all subentries in all namingContexts
155 Set<String> suffixes = this.nexus.listSuffixes( null );
156 ExprNode filter = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, new StringValue(
157 SchemaConstants.SUBENTRY_OC ) );
158 SearchControls controls = new SearchControls();
159 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
160 controls.setReturningAttributes( new String[]
161 { SchemaConstants.SUBTREE_SPECIFICATION_AT, SchemaConstants.OBJECT_CLASS_AT } );
162
163 // search each namingContext for subentries
164 for ( String suffix:suffixes )
165 {
166 DN suffixDn = new DN( suffix );
167 suffixDn.normalize( schemaManager.getNormalizerMapping() );
168
169 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
170 adminDn.normalize( schemaManager.getNormalizerMapping() );
171 CoreSession adminSession = new DefaultCoreSession(
172 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
173
174 SearchOperationContext searchOperationContext = new SearchOperationContext( adminSession, suffixDn,
175 filter, controls );
176 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
177
178 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
179
180 while ( subentries.next() )
181 {
182 ServerEntry subentry = subentries.get();
183 DN dnName = subentry.getDn();
184
185 String subtree = subentry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
186 SubtreeSpecification ss;
187
188 try
189 {
190 ss = ssParser.parse( subtree );
191 }
192 catch ( Exception e )
193 {
194 LOG.warn( "Failed while parsing subtreeSpecification for " + dnName );
195 continue;
196 }
197
198 dnName.normalize( schemaManager.getNormalizerMapping() );
199 subentryCache.setSubentry( dnName.getNormName(), ss, getSubentryTypes( subentry ) );
200 }
201 }
202 }
203
204
205 private int getSubentryTypes( ServerEntry subentry ) throws Exception
206 {
207 int types = 0;
208
209 EntryAttribute oc = subentry.get( SchemaConstants.OBJECT_CLASS_AT );
210
211 if ( oc == null )
212 {
213 throw new LdapSchemaViolationException( ResultCodeEnum.OBJECT_CLASS_VIOLATION,
214 I18n.err( I18n.ERR_305 ) );
215 }
216
217 if ( oc.contains( SchemaConstants.ACCESS_CONTROL_SUBENTRY_OC ) )
218 {
219 types |= Subentry.ACCESS_CONTROL_SUBENTRY;
220 }
221
222 if ( oc.contains( "subschema" ) )
223 {
224 types |= Subentry.SCHEMA_SUBENTRY;
225 }
226
227 if ( oc.contains( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRY_OC ) )
228 {
229 types |= Subentry.COLLECTIVE_SUBENTRY;
230 }
231
232 if ( oc.contains( ApacheSchemaConstants.TRIGGER_EXECUTION_SUBENTRY_OC ) )
233 {
234 types |= Subentry.TRIGGER_SUBENTRY;
235 }
236
237 return types;
238 }
239
240
241 // -----------------------------------------------------------------------
242 // Methods/Code dealing with Subentry Visibility
243 // -----------------------------------------------------------------------
244
245
246 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext )
247 throws Exception
248 {
249 EntryFilteringCursor cursor = nextInterceptor.list( opContext );
250
251 if ( !isSubentryVisible( opContext ) )
252 {
253 cursor.addEntryFilter( new HideSubentriesFilter() );
254 }
255
256 return cursor;
257 }
258
259
260 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext )
261 throws Exception
262 {
263 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
264
265 // object scope searches by default return subentries
266 if ( opContext.getScope() == SearchScope.OBJECT )
267 {
268 return cursor;
269 }
270
271 // for subtree and one level scope we filter
272 if ( !isSubentryVisible( opContext ) )
273 {
274 cursor.addEntryFilter( new HideSubentriesFilter() );
275 }
276 else
277 {
278 cursor.addEntryFilter( new HideEntriesFilter() );
279 }
280
281 return cursor;
282 }
283
284
285 /**
286 * Checks to see if subentries for the search and list operations should be
287 * made visible based on the availability of the search request control
288 *
289 * @param invocation the invocation object to use for determining subentry visibility
290 * @return true if subentries should be visible, false otherwise
291 * @throws Exception if there are problems accessing request controls
292 */
293 private boolean isSubentryVisible( OperationContext opContext ) throws Exception
294 {
295 if ( !opContext.hasRequestControls() )
296 {
297 return false;
298 }
299
300 // found the subentry request control so we return its value
301 if ( opContext.hasRequestControl( SUBENTRY_CONTROL ) )
302 {
303 SubentriesControl subentriesControl = ( SubentriesControl ) opContext.getRequestControl( SUBENTRY_CONTROL );
304 return subentriesControl.isVisible();
305 }
306
307 return false;
308 }
309
310
311 // -----------------------------------------------------------------------
312 // Methods dealing with entry and subentry addition
313 // -----------------------------------------------------------------------
314
315 /**
316 * Evaluates the set of subentry subtrees upon an entry and returns the
317 * operational subentry attributes that will be added to the entry if
318 * added at the dn specified.
319 *
320 * @param dn the normalized distinguished name of the entry
321 * @param entryAttrs the entry attributes are generated for
322 * @return the set of subentry op attrs for an entry
323 * @throws Exception if there are problems accessing entry information
324 */
325 public ServerEntry getSubentryAttributes( DN dn, ServerEntry entryAttrs ) throws Exception
326 {
327 ServerEntry subentryAttrs = new DefaultServerEntry( schemaManager, dn );
328 Iterator<String> list = subentryCache.nameIterator();
329
330 while ( list.hasNext() )
331 {
332 String subentryDnStr = list.next();
333 DN subentryDn = new DN( subentryDnStr );
334 DN apDn = ( DN ) subentryDn.clone();
335 apDn.remove( apDn.size() - 1 );
336 Subentry subentry = subentryCache.getSubentry( subentryDnStr );
337 SubtreeSpecification ss = subentry.getSubtreeSpecification();
338
339 if ( evaluator.evaluate( ss, apDn, dn, entryAttrs ) )
340 {
341 EntryAttribute operational;
342
343 if ( subentry.isAccessControlSubentry() )
344 {
345 operational = subentryAttrs.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
346
347 if ( operational == null )
348 {
349 operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT,
350 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
351 subentryAttrs.put( operational );
352 }
353
354 operational.add( subentryDn.getNormName() );
355 }
356 if ( subentry.isSchemaSubentry() )
357 {
358 operational = subentryAttrs.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
359
360 if ( operational == null )
361 {
362 operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager
363 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
364 subentryAttrs.put( operational );
365 }
366
367 operational.add( subentryDn.getNormName() );
368 }
369 if ( subentry.isCollectiveSubentry() )
370 {
371 operational = subentryAttrs.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
372
373 if ( operational == null )
374 {
375 operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
376 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
377 subentryAttrs.put( operational );
378 }
379
380 operational.add( subentryDn.getNormName() );
381 }
382 if ( subentry.isTriggerSubentry() )
383 {
384 operational = subentryAttrs.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
385
386 if ( operational == null )
387 {
388 operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT,
389 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
390 subentryAttrs.put( operational );
391 }
392
393 operational.add( subentryDn.getNormName() );
394 }
395 }
396 }
397
398 return subentryAttrs;
399 }
400
401
402 public void add( NextInterceptor next, AddOperationContext addContext ) throws Exception
403 {
404 DN name = addContext.getDn();
405 ClonedServerEntry entry = addContext.getEntry();
406
407 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
408
409 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
410 {
411 // get the name of the administrative point and its administrativeRole attributes
412 DN apName = ( DN ) name.clone();
413 apName.remove( name.size() - 1 );
414 ServerEntry ap = addContext.lookup( apName, ByPassConstants.LOOKUP_BYPASS );
415 EntryAttribute administrativeRole = ap.get( "administrativeRole" );
416
417 // check that administrativeRole has something valid in it for us
418 if ( administrativeRole == null || administrativeRole.size() <= 0 )
419 {
420 throw new LdapNoSuchAttributeException( I18n.err( I18n.ERR_306, apName ) );
421 }
422
423 /* ----------------------------------------------------------------
424 * Build the set of operational attributes to be injected into
425 * entries that are contained within the subtree repesented by this
426 * new subentry. In the process we make sure the proper roles are
427 * supported by the administrative point to allow the addition of
428 * this new subentry.
429 * ----------------------------------------------------------------
430 */
431 Subentry subentry = new Subentry();
432 subentry.setTypes( getSubentryTypes( entry ) );
433 ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
434
435 /* ----------------------------------------------------------------
436 * Parse the subtreeSpecification of the subentry and add it to the
437 * SubtreeSpecification cache. If the parse succeeds we continue
438 * to add the entry to the DIT. Thereafter we search out entries
439 * to modify the subentry operational attributes of.
440 * ----------------------------------------------------------------
441 */
442 String subtree = entry.get( SchemaConstants.SUBTREE_SPECIFICATION_AT ).getString();
443 SubtreeSpecification ss;
444
445 try
446 {
447 ss = ssParser.parse( subtree );
448 }
449 catch ( Exception e )
450 {
451 String msg = I18n.err( I18n.ERR_307, name.getName() );
452 LOG.warn( msg );
453 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
454 }
455
456 subentryCache.setSubentry( name.getNormName(), ss, getSubentryTypes( entry ) );
457
458 next.add( addContext );
459
460 /* ----------------------------------------------------------------
461 * Find the baseDn for the subentry and use that to search the tree
462 * while testing each entry returned for inclusion within the
463 * subtree of the subentry's subtreeSpecification. All included
464 * entries will have their operational attributes merged with the
465 * operational attributes calculated above.
466 * ----------------------------------------------------------------
467 */
468 DN baseDn = ( DN ) apName.clone();
469 baseDn.addAll( ss.getBase() );
470
471 ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT_OID ); // (objectClass=*)
472 SearchControls controls = new SearchControls();
473 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
474 controls.setReturningAttributes( new String[]
475 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
476
477 SearchOperationContext searchOperationContext = new SearchOperationContext( addContext.getSession(), baseDn,
478 filter, controls );
479 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
480
481 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
482
483 while ( subentries.next() )
484 {
485 ServerEntry candidate = subentries.get();
486 DN dn = candidate.getDn();
487 dn.normalize( schemaManager.getNormalizerMapping() );
488
489 if ( evaluator.evaluate( ss, apName, dn, candidate ) )
490 {
491 nexus.modify( new ModifyOperationContext( addContext.getSession(), dn,
492 getOperationalModsForAdd( candidate, operational ) ) );
493 }
494 }
495
496 // TODO why are we doing this here if we got the entry from the
497 // opContext in the first place - got to look into this
498 addContext.setEntry( entry );
499 }
500 else
501 {
502 Iterator<String> list = subentryCache.nameIterator();
503
504 while ( list.hasNext() )
505 {
506 String subentryDnStr = list.next();
507 DN subentryDn = new DN( subentryDnStr );
508 DN apDn = ( DN ) subentryDn.clone();
509 apDn.remove( apDn.size() - 1 );
510 Subentry subentry = subentryCache.getSubentry( subentryDnStr );
511 SubtreeSpecification ss = subentry.getSubtreeSpecification();
512
513 if ( evaluator.evaluate( ss, apDn, name, entry ) )
514 {
515 EntryAttribute operational;
516
517 if ( subentry.isAccessControlSubentry() )
518 {
519 operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT );
520
521 if ( operational == null )
522 {
523 operational = new DefaultServerAttribute( schemaManager
524 .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
525 entry.put( operational );
526 }
527
528 operational.add( subentryDn.getNormName() );
529 }
530
531 if ( subentry.isSchemaSubentry() )
532 {
533 operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT );
534
535 if ( operational == null )
536 {
537 operational = new DefaultServerAttribute( schemaManager
538 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
539 entry.put( operational );
540 }
541
542 operational.add( subentryDn.getNormName() );
543 }
544
545 if ( subentry.isCollectiveSubentry() )
546 {
547 operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT );
548
549 if ( operational == null )
550 {
551 operational = new DefaultServerAttribute( schemaManager
552 .lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
553 entry.put( operational );
554 }
555
556 operational.add( subentryDn.getNormName() );
557 }
558
559 if ( subentry.isTriggerSubentry() )
560 {
561 operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT );
562
563 if ( operational == null )
564 {
565 operational = new DefaultServerAttribute( schemaManager
566 .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
567 entry.put( operational );
568 }
569
570 operational.add( subentryDn.getNormName() );
571 }
572 }
573 }
574
575 // TODO why are we doing this here if we got the entry from the
576 // opContext in the first place - got to look into this
577 addContext.setEntry( entry );
578
579 next.add( addContext );
580 }
581 }
582
583
584 // -----------------------------------------------------------------------
585 // Methods dealing subentry deletion
586 // -----------------------------------------------------------------------
587
588 public void delete( NextInterceptor next, DeleteOperationContext opContext ) throws Exception
589 {
590 DN name = opContext.getDn();
591 ServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
592 EntryAttribute objectClasses = entry.get( objectClassType );
593
594 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
595 {
596 SubtreeSpecification ss = subentryCache.removeSubentry( name.getNormName() ).getSubtreeSpecification();
597 next.delete( opContext );
598
599 /* ----------------------------------------------------------------
600 * Find the baseDn for the subentry and use that to search the tree
601 * for all entries included by the subtreeSpecification. Then we
602 * check the entry for subentry operational attribute that contain
603 * the DN of the subentry. These are the subentry operational
604 * attributes we remove from the entry in a modify operation.
605 * ----------------------------------------------------------------
606 */
607 DN apName = ( DN ) name.clone();
608 apName.remove( name.size() - 1 );
609 DN baseDn = ( DN ) apName.clone();
610 baseDn.addAll( ss.getBase() );
611
612 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) );
613 SearchControls controls = new SearchControls();
614 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
615 controls.setReturningAttributes( new String[]
616 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
617
618 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn,
619 filter, controls );
620 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
621
622 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
623
624 while ( subentries.next() )
625 {
626 ServerEntry candidate = subentries.get();
627 DN dn = new DN( candidate.getDn() );
628 dn.normalize( schemaManager.getNormalizerMapping() );
629
630 if ( evaluator.evaluate( ss, apName, dn, candidate ) )
631 {
632 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
633 getOperationalModsForRemove( name, candidate ) ) );
634 }
635 }
636 }
637 else
638 {
639 next.delete( opContext );
640 }
641 }
642
643
644 // -----------------------------------------------------------------------
645 // Methods dealing subentry name changes
646 // -----------------------------------------------------------------------
647
648 /**
649 * Checks to see if an entry being renamed has a descendant that is an
650 * administrative point.
651 *
652 * @param name the name of the entry which is used as the search base
653 * @return true if name is an administrative point or one of its descendants
654 * are, false otherwise
655 * @throws Exception if there are errors while searching the directory
656 */
657 private boolean hasAdministrativeDescendant( OperationContext opContext, DN name ) throws Exception
658 {
659 ExprNode filter = new PresenceNode( "administrativeRole" );
660 SearchControls controls = new SearchControls();
661 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
662
663 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), name,
664 filter, controls );
665 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
666
667 EntryFilteringCursor aps = nexus.search( searchOperationContext );
668
669 if ( aps.next() )
670 {
671 aps.close();
672 return true;
673 }
674
675 return false;
676 }
677
678
679 private List<Modification> getModsOnEntryRdnChange( DN oldName, DN newName, ServerEntry entry )
680 throws Exception
681 {
682 List<Modification> modList = new ArrayList<Modification>();
683
684 /*
685 * There are two different situations warranting action. Firt if
686 * an ss evalutating to true with the old name no longer evalutates
687 * to true with the new name. This would be caused by specific chop
688 * exclusions that effect the new name but did not effect the old
689 * name. In this case we must remove subentry operational attribute
690 * values associated with the dn of that subentry.
691 *
692 * In the second case an ss selects the entry with the new name when
693 * it did not previously with the old name. Again this situation
694 * would be caused by chop exclusions. In this case we must add subentry
695 * operational attribute values with the dn of this subentry.
696 */
697 Iterator<String> subentries = subentryCache.nameIterator();
698
699 while ( subentries.hasNext() )
700 {
701 String subentryDn = subentries.next();
702 DN apDn = new DN( subentryDn );
703 apDn.remove( apDn.size() - 1 );
704 SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification();
705 boolean isOldNameSelected = evaluator.evaluate( ss, apDn, oldName, entry );
706 boolean isNewNameSelected = evaluator.evaluate( ss, apDn, newName, entry );
707
708 if ( isOldNameSelected == isNewNameSelected )
709 {
710 continue;
711 }
712
713 // need to remove references to the subentry
714 if ( isOldNameSelected && !isNewNameSelected )
715 {
716 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
717 {
718 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
719 EntryAttribute opAttr = entry.get( aSUBENTRY_OPATTRS );
720
721 if ( opAttr != null )
722 {
723 opAttr = opAttr.clone();
724 opAttr.remove( subentryDn );
725
726 if ( opAttr.size() < 1 )
727 {
728 op = ModificationOperation.REMOVE_ATTRIBUTE;
729 }
730
731 modList.add( new ServerModification( op, opAttr ) );
732 }
733 }
734 }
735 // need to add references to the subentry
736 else if ( isNewNameSelected && !isOldNameSelected )
737 {
738 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
739 {
740 ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
741 EntryAttribute opAttr = new DefaultServerAttribute( aSUBENTRY_OPATTRS, schemaManager
742 .lookupAttributeTypeRegistry( aSUBENTRY_OPATTRS ) );
743 opAttr.add( subentryDn );
744 modList.add( new ServerModification( op, opAttr ) );
745 }
746 }
747 }
748
749 return modList;
750 }
751
752
753 public void rename( NextInterceptor next, RenameOperationContext opContext ) throws Exception
754 {
755 DN name = opContext.getDn();
756
757 ServerEntry entry = (ServerEntry)opContext.getEntry().getClonedEntry();
758
759 EntryAttribute objectClasses = entry.get( objectClassType );
760
761 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
762 {
763 // @Todo To be reviewed !!!
764 Subentry subentry = subentryCache.getSubentry( name.getNormName() );
765 SubtreeSpecification ss = subentry.getSubtreeSpecification();
766 DN apName = ( DN ) name.clone();
767 apName.remove( apName.size() - 1 );
768 DN baseDn = ( DN ) apName.clone();
769 baseDn.addAll( ss.getBase() );
770 DN newName = ( DN ) name.clone();
771 newName.remove( newName.size() - 1 );
772
773 newName.add( opContext.getNewRdn() );
774
775 String newNormName = newName.getNormName();
776 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
777 next.rename( opContext );
778
779 subentry = subentryCache.getSubentry( newNormName );
780 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) );
781 SearchControls controls = new SearchControls();
782 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
783 controls.setReturningAttributes( new String[]
784 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
785
786 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn,
787 filter, controls );
788 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
789
790 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
791
792 while ( subentries.next() )
793 {
794 ServerEntry candidate = subentries.get();
795 DN dn = candidate.getDn();
796 dn.normalize( schemaManager.getNormalizerMapping() );
797
798
799 if ( evaluator.evaluate( ss, apName, dn, candidate ) )
800 {
801 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
802 getOperationalModsForReplace( name, newName, subentry, candidate ) ) );
803 }
804 }
805 }
806 else
807 {
808 if ( hasAdministrativeDescendant( opContext, name ) )
809 {
810 String msg = I18n.err( I18n.ERR_308 );
811 LOG.warn( msg );
812 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
813 }
814
815 next.rename( opContext );
816
817 // calculate the new DN now for use below to modify subentry operational
818 // attributes contained within this regular entry with name changes
819 DN newName = opContext.getNewDn();
820
821 List<Modification> mods = getModsOnEntryRdnChange( name, newName, entry );
822
823 if ( mods.size() > 0 )
824 {
825 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) );
826 }
827 }
828 }
829
830
831 public void moveAndRename( NextInterceptor next, MoveAndRenameOperationContext opContext ) throws Exception
832 {
833 DN oriChildName = opContext.getDn();
834 DN parent = opContext.getParent();
835
836 ServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
837
838 EntryAttribute objectClasses = entry.get( objectClassType );
839
840 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
841 {
842 Subentry subentry = subentryCache.getSubentry( oriChildName.getNormName() );
843 SubtreeSpecification ss = subentry.getSubtreeSpecification();
844 DN apName = ( DN ) oriChildName.clone();
845 apName.remove( apName.size() - 1 );
846 DN baseDn = ( DN ) apName.clone();
847 baseDn.addAll( ss.getBase() );
848 DN newName = ( DN ) parent.clone();
849 newName.remove( newName.size() - 1 );
850
851 newName.add( opContext.getNewRdn() );
852
853 String newNormName = newName.getNormName();
854 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
855 next.moveAndRename( opContext );
856
857 subentry = subentryCache.getSubentry( newNormName );
858
859 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) );
860 SearchControls controls = new SearchControls();
861 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
862 controls.setReturningAttributes( new String[]
863 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
864
865 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn,
866 filter, controls );
867 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
868
869 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
870
871 while ( subentries.next() )
872 {
873 ServerEntry candidate = subentries.get();
874 DN dn = candidate.getDn();
875 dn.normalize( schemaManager.getNormalizerMapping() );
876
877 if ( evaluator.evaluate( ss, apName, dn, candidate ) )
878 {
879 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
880 getOperationalModsForReplace( oriChildName, newName, subentry, candidate ) ) );
881 }
882 }
883 }
884 else
885 {
886 if ( hasAdministrativeDescendant( opContext, oriChildName ) )
887 {
888 String msg = I18n.err( I18n.ERR_308 );
889 LOG.warn( msg );
890 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
891 }
892
893 next.moveAndRename( opContext );
894
895 // calculate the new DN now for use below to modify subentry operational
896 // attributes contained within this regular entry with name changes
897 DN newName = ( DN ) parent.clone();
898 newName.add( opContext.getNewRdn() );
899 newName.normalize( schemaManager.getNormalizerMapping() );
900 List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
901
902 if ( mods.size() > 0 )
903 {
904 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) );
905 }
906 }
907 }
908
909
910 public void move( NextInterceptor next, MoveOperationContext opContext ) throws Exception
911 {
912 DN oriChildName = opContext.getDn();
913 DN newParentName = opContext.getParent();
914
915 ServerEntry entry = opContext.lookup( oriChildName, ByPassConstants.LOOKUP_BYPASS );
916
917 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
918
919 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
920 {
921 Subentry subentry = subentryCache.getSubentry( oriChildName.getNormName() );
922 SubtreeSpecification ss = subentry.getSubtreeSpecification();
923 DN apName = ( DN ) oriChildName.clone();
924 apName.remove( apName.size() - 1 );
925 DN baseDn = ( DN ) apName.clone();
926 baseDn.addAll( ss.getBase() );
927 DN newName = ( DN ) newParentName.clone();
928 newName.remove( newName.size() - 1 );
929 newName.add( newParentName.get( newParentName.size() - 1 ) );
930
931 String newNormName = newName.getNormName();
932 subentryCache.setSubentry( newNormName, ss, subentry.getTypes() );
933 next.move( opContext );
934
935 subentry = subentryCache.getSubentry( newNormName );
936
937 ExprNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
938 SearchControls controls = new SearchControls();
939 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
940 controls.setReturningAttributes( new String[]
941 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
942
943 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), baseDn,
944 filter, controls );
945 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
946
947 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
948
949 while ( subentries.next() )
950 {
951 ServerEntry candidate = subentries.get();
952 DN dn = candidate.getDn();
953 dn.normalize( schemaManager.getNormalizerMapping() );
954
955 if ( evaluator.evaluate( ss, apName, dn, candidate ) )
956 {
957 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
958 getOperationalModsForReplace( oriChildName, newName, subentry, candidate ) ) );
959 }
960 }
961 }
962 else
963 {
964 if ( hasAdministrativeDescendant( opContext, oriChildName ) )
965 {
966 String msg = I18n.err( I18n.ERR_308 );
967 LOG.warn( msg );
968 throw new LdapSchemaViolationException( ResultCodeEnum.NOT_ALLOWED_ON_RDN, msg );
969 }
970
971 next.move( opContext );
972
973 // calculate the new DN now for use below to modify subentry operational
974 // attributes contained within this regular entry with name changes
975 DN newName = ( DN ) newParentName.clone();
976 newName.add( oriChildName.get( oriChildName.size() - 1 ) );
977 List<Modification> mods = getModsOnEntryRdnChange( oriChildName, newName, entry );
978
979 if ( mods.size() > 0 )
980 {
981 nexus.modify( new ModifyOperationContext( opContext.getSession(), newName, mods ) );
982 }
983 }
984 }
985
986
987 // -----------------------------------------------------------------------
988 // Methods dealing subentry modification
989 // -----------------------------------------------------------------------
990
991 private int getSubentryTypes( ServerEntry entry, List<Modification> mods ) throws Exception
992 {
993 EntryAttribute ocFinalState = entry.get( SchemaConstants.OBJECT_CLASS_AT ).clone();
994
995 for ( Modification mod : mods )
996 {
997 if ( mod.getAttribute().getId().equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) )
998 {
999 switch ( mod.getOperation() )
1000 {
1001 case ADD_ATTRIBUTE:
1002 for ( Value<?> value : mod.getAttribute() )
1003 {
1004 ocFinalState.add( value.getString() );
1005 }
1006
1007 break;
1008
1009 case REMOVE_ATTRIBUTE:
1010 for ( Value<?> value : mod.getAttribute() )
1011 {
1012 ocFinalState.remove( value.getString() );
1013 }
1014
1015 break;
1016
1017 case REPLACE_ATTRIBUTE:
1018 ocFinalState = mod.getAttribute();
1019 break;
1020 }
1021 }
1022 }
1023
1024 ServerEntry attrs = new DefaultServerEntry( schemaManager, DN.EMPTY_DN );
1025 attrs.put( ocFinalState );
1026 return getSubentryTypes( attrs );
1027 }
1028
1029
1030 public void modify( NextInterceptor next, ModifyOperationContext opContext ) throws Exception
1031 {
1032 DN name = opContext.getDn();
1033 List<Modification> mods = opContext.getModItems();
1034
1035 ServerEntry entry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
1036
1037 ServerEntry oldEntry = ( ServerEntry ) entry.clone();
1038 EntryAttribute objectClasses = entry.get( objectClassType );
1039 boolean isSubtreeSpecificationModification = false;
1040 Modification subtreeMod = null;
1041
1042 for ( Modification mod : mods )
1043 {
1044 if ( SchemaConstants.SUBTREE_SPECIFICATION_AT.equalsIgnoreCase( mod.getAttribute().getId() ) )
1045 {
1046 isSubtreeSpecificationModification = true;
1047 subtreeMod = mod;
1048 }
1049 }
1050
1051 if ( objectClasses.contains( SchemaConstants.SUBENTRY_OC ) && isSubtreeSpecificationModification )
1052 {
1053 SubtreeSpecification ssOld = subentryCache.removeSubentry( name.getNormName() ).getSubtreeSpecification();
1054 SubtreeSpecification ssNew;
1055
1056 try
1057 {
1058 ssNew = ssParser.parse( subtreeMod.getAttribute().getString() );
1059 }
1060 catch ( Exception e )
1061 {
1062 String msg = I18n.err( I18n.ERR_71 );
1063 LOG.error( msg, e );
1064 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
1065 }
1066
1067 subentryCache.setSubentry( name.getNormName(), ssNew, getSubentryTypes( entry, mods ) );
1068 next.modify( opContext );
1069
1070 // search for all entries selected by the old SS and remove references to subentry
1071 DN apName = ( DN ) name.clone();
1072 apName.remove( apName.size() - 1 );
1073 DN oldBaseDn = ( DN ) apName.clone();
1074 oldBaseDn.addAll( ssOld.getBase() );
1075 ExprNode filter = new PresenceNode( schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASS_AT ) );
1076 SearchControls controls = new SearchControls();
1077 controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
1078 controls.setReturningAttributes( new String[]
1079 { SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES, SchemaConstants.ALL_USER_ATTRIBUTES } );
1080
1081 SearchOperationContext searchOperationContext = new SearchOperationContext( opContext.getSession(), oldBaseDn,
1082 filter, controls );
1083 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
1084
1085 EntryFilteringCursor subentries = nexus.search( searchOperationContext );
1086
1087 while ( subentries.next() )
1088 {
1089 ServerEntry candidate = subentries.get();
1090 DN dn = candidate.getDn();
1091 dn.normalize( schemaManager.getNormalizerMapping() );
1092
1093 if ( evaluator.evaluate( ssOld, apName, dn, candidate ) )
1094 {
1095 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
1096 getOperationalModsForRemove( name, candidate ) ) );
1097 }
1098 }
1099
1100 // search for all selected entries by the new SS and add references to subentry
1101 Subentry subentry = subentryCache.getSubentry( name.getNormName() );
1102 ServerEntry operational = getSubentryOperatationalAttributes( name, subentry );
1103 DN newBaseDn = ( DN ) apName.clone();
1104 newBaseDn.addAll( ssNew.getBase() );
1105
1106 searchOperationContext = new SearchOperationContext( opContext.getSession(), newBaseDn,
1107 filter, controls );
1108 searchOperationContext.setAliasDerefMode( AliasDerefMode.NEVER_DEREF_ALIASES );
1109
1110 subentries = nexus.search( searchOperationContext );
1111
1112 while ( subentries.next() )
1113 {
1114 ServerEntry candidate = subentries.get();
1115 DN dn = candidate.getDn();
1116 dn.normalize( schemaManager.getNormalizerMapping() );
1117
1118 if ( evaluator.evaluate( ssNew, apName, dn, candidate ) )
1119 {
1120 nexus.modify( new ModifyOperationContext( opContext.getSession(), dn,
1121 getOperationalModsForAdd( candidate, operational ) ) );
1122 }
1123 }
1124 }
1125 else
1126 {
1127 next.modify( opContext );
1128
1129 if ( !objectClasses.contains( SchemaConstants.SUBENTRY_OC ) )
1130 {
1131 ServerEntry newEntry = opContext.lookup( name, ByPassConstants.LOOKUP_BYPASS );
1132
1133 List<Modification> subentriesOpAttrMods = getModsOnEntryModification( name, oldEntry, newEntry );
1134
1135 if ( subentriesOpAttrMods.size() > 0 )
1136 {
1137 nexus.modify( new ModifyOperationContext( opContext.getSession(), name, subentriesOpAttrMods ) );
1138 }
1139 }
1140 }
1141 }
1142
1143
1144 // -----------------------------------------------------------------------
1145 // Utility Methods
1146 // -----------------------------------------------------------------------
1147
1148 private List<Modification> getOperationalModsForReplace( DN oldName, DN newName, Subentry subentry,
1149 ServerEntry entry ) throws Exception
1150 {
1151 List<Modification> modList = new ArrayList<Modification>();
1152
1153 EntryAttribute operational;
1154
1155 if ( subentry.isAccessControlSubentry() )
1156 {
1157 operational = entry.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).clone();
1158
1159 if ( operational == null )
1160 {
1161 operational = new DefaultServerAttribute( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, schemaManager
1162 .lookupAttributeTypeRegistry( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) );
1163 operational.add( newName.toString() );
1164 }
1165 else
1166 {
1167 operational.remove( oldName.toString() );
1168 operational.add( newName.toString() );
1169 }
1170
1171 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
1172 }
1173
1174 if ( subentry.isSchemaSubentry() )
1175 {
1176 operational = entry.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).clone();
1177
1178 if ( operational == null )
1179 {
1180 operational = new DefaultServerAttribute( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, schemaManager
1181 .lookupAttributeTypeRegistry( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) );
1182 operational.add( newName.toString() );
1183 }
1184 else
1185 {
1186 operational.remove( oldName.toString() );
1187 operational.add( newName.toString() );
1188 }
1189
1190 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
1191 }
1192
1193 if ( subentry.isCollectiveSubentry() )
1194 {
1195 operational = entry.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).clone();
1196
1197 if ( operational == null )
1198 {
1199 operational = new DefaultServerAttribute( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT,
1200 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) );
1201 operational.add( newName.toString() );
1202 }
1203 else
1204 {
1205 operational.remove( oldName.toString() );
1206 operational.add( newName.toString() );
1207 }
1208
1209 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
1210 }
1211
1212 if ( subentry.isTriggerSubentry() )
1213 {
1214 operational = entry.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).clone();
1215
1216 if ( operational == null )
1217 {
1218 operational = new DefaultServerAttribute( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, schemaManager
1219 .lookupAttributeTypeRegistry( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) );
1220 operational.add( newName.toString() );
1221 }
1222 else
1223 {
1224 operational.remove( oldName.toString() );
1225 operational.add( newName.toString() );
1226 }
1227
1228 modList.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, operational ) );
1229 }
1230
1231 return modList;
1232 }
1233
1234
1235 /**
1236 * Gets the subschema operational attributes to be added to or removed from
1237 * an entry selected by a subentry's subtreeSpecification.
1238 *
1239 * @param name the normalized distinguished name of the subentry (the value of op attrs)
1240 * @param subentry the subentry to get attributes from
1241 * @return the set of attributes to be added or removed from entries
1242 */
1243 private ServerEntry getSubentryOperatationalAttributes( DN name, Subentry subentry ) throws Exception
1244 {
1245 ServerEntry operational = new DefaultServerEntry( schemaManager, name );
1246
1247 if ( subentry.isAccessControlSubentry() )
1248 {
1249 if ( operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ) == null )
1250 {
1251 operational.put( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT, name.getNormName() );
1252 }
1253 else
1254 {
1255 operational.get( SchemaConstants.ACCESS_CONTROL_SUBENTRIES_AT ).add( name.getNormName() );
1256 }
1257 }
1258 if ( subentry.isSchemaSubentry() )
1259 {
1260 if ( operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ) == null )
1261 {
1262 operational.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, name.getNormName() );
1263 }
1264 else
1265 {
1266 operational.get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).add( name.getNormName() );
1267 }
1268 }
1269 if ( subentry.isCollectiveSubentry() )
1270 {
1271 if ( operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ) == null )
1272 {
1273 operational.put( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT, name.getNormName() );
1274 }
1275 else
1276 {
1277 operational.get( SchemaConstants.COLLECTIVE_ATTRIBUTE_SUBENTRIES_AT ).add( name.getNormName() );
1278 }
1279 }
1280 if ( subentry.isTriggerSubentry() )
1281 {
1282 if ( operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ) == null )
1283 {
1284 operational.put( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT, name.getNormName() );
1285 }
1286 else
1287 {
1288 operational.get( SchemaConstants.TRIGGER_EXECUTION_SUBENTRIES_AT ).add( name.getNormName() );
1289 }
1290 }
1291
1292 return operational;
1293 }
1294
1295
1296 /**
1297 * Calculates the subentry operational attributes to remove from a candidate
1298 * entry selected by a subtreeSpecification. When we remove a subentry we
1299 * must remove the operational attributes in the entries that were once selected
1300 * by the subtree specification of that subentry. To do so we must perform
1301 * a modify operation with the set of modifications to perform. This method
1302 * calculates those modifications.
1303 *
1304 * @param subentryDn the distinguished name of the subentry
1305 * @param candidate the candidate entry to removed from the
1306 * @return the set of modifications required to remove an entry's reference to
1307 * a subentry
1308 */
1309 private List<Modification> getOperationalModsForRemove( DN subentryDn, ServerEntry candidate )
1310 throws Exception
1311 {
1312 List<Modification> modList = new ArrayList<Modification>();
1313 String dn = subentryDn.getNormName();
1314
1315 for ( String opAttrId : SUBENTRY_OPATTRS )
1316 {
1317 EntryAttribute opAttr = candidate.get( opAttrId );
1318
1319 if ( ( opAttr != null ) && opAttr.contains( dn ) )
1320 {
1321 AttributeType attributeType = schemaManager.lookupAttributeTypeRegistry( opAttrId );
1322 EntryAttribute attr = new DefaultServerAttribute( opAttrId, attributeType, dn );
1323 modList.add( new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) );
1324 }
1325 }
1326
1327 return modList;
1328 }
1329
1330
1331 /**
1332 * Calculates the subentry operational attributes to add or replace from
1333 * a candidate entry selected by a subtree specification. When a subentry
1334 * is added or it's specification is modified some entries must have new
1335 * operational attributes added to it to point back to the associated
1336 * subentry. To do so a modify operation must be performed on entries
1337 * selected by the subtree specification. This method calculates the
1338 * modify operation to be performed on the entry.
1339 *
1340 * @param entry the entry being modified
1341 * @param operational the set of operational attributes supported by the AP
1342 * of the subentry
1343 * @return the set of modifications needed to update the entry
1344 * @throws Exception if there are probelms accessing modification items
1345 */
1346 public List<Modification> getOperationalModsForAdd( ServerEntry entry, ServerEntry operational )
1347 throws Exception
1348 {
1349 List<Modification> modList = new ArrayList<Modification>();
1350
1351 for ( AttributeType attributeType : operational.getAttributeTypes() )
1352 {
1353 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
1354 EntryAttribute result = new DefaultServerAttribute( attributeType );
1355 EntryAttribute opAttrAdditions = operational.get( attributeType );
1356 EntryAttribute opAttrInEntry = entry.get( attributeType );
1357
1358 for ( Value<?> value : opAttrAdditions )
1359 {
1360 result.add( value );
1361 }
1362
1363 if ( opAttrInEntry != null && opAttrInEntry.size() > 0 )
1364 {
1365 for ( Value<?> value : opAttrInEntry )
1366 {
1367 result.add( value );
1368 }
1369 }
1370 else
1371 {
1372 op = ModificationOperation.ADD_ATTRIBUTE;
1373 }
1374
1375 modList.add( new ServerModification( op, result ) );
1376 }
1377
1378 return modList;
1379 }
1380
1381 /**
1382 * SearchResultFilter used to filter out subentries based on objectClass values.
1383 */
1384 public class HideSubentriesFilter implements EntryFilter
1385 {
1386 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry )
1387 throws Exception
1388 {
1389 String dn = entry.getDn().getNormName();
1390
1391 // see if we can get a match without normalization
1392 if ( subentryCache.hasSubentry( dn ) )
1393 {
1394 return false;
1395 }
1396
1397 // see if we can use objectclass if present
1398 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1399
1400 if ( objectClasses != null )
1401 {
1402 return !objectClasses.contains( SchemaConstants.SUBENTRY_OC );
1403 }
1404
1405 DN ndn = new DN( dn );
1406 ndn.normalize( schemaManager.getNormalizerMapping() );
1407 String normalizedDn = ndn.getNormName();
1408 return !subentryCache.hasSubentry( normalizedDn );
1409 }
1410 }
1411
1412 /**
1413 * SearchResultFilter used to filter out normal entries but shows subentries based on
1414 * objectClass values.
1415 */
1416 public class HideEntriesFilter implements EntryFilter
1417 {
1418 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry )
1419 throws Exception
1420 {
1421 String dn = entry.getDn().getNormName();
1422
1423 // see if we can get a match without normalization
1424 if ( subentryCache.hasSubentry( dn ) )
1425 {
1426 return true;
1427 }
1428
1429 // see if we can use objectclass if present
1430 EntryAttribute objectClasses = entry.get( SchemaConstants.OBJECT_CLASS_AT );
1431
1432 if ( objectClasses != null )
1433 {
1434 return objectClasses.contains( SchemaConstants.SUBENTRY_OC );
1435 }
1436
1437 DN ndn = new DN( dn );
1438 ndn.normalize( schemaManager.getNormalizerMapping() );
1439 return subentryCache.hasSubentry( ndn.getNormName() );
1440 }
1441 }
1442
1443
1444 private List<Modification> getModsOnEntryModification( DN name, ServerEntry oldEntry, ServerEntry newEntry )
1445 throws Exception
1446 {
1447 List<Modification> modList = new ArrayList<Modification>();
1448
1449 Iterator<String> subentries = subentryCache.nameIterator();
1450
1451 while ( subentries.hasNext() )
1452 {
1453 String subentryDn = subentries.next();
1454 DN apDn = new DN( subentryDn );
1455 apDn.remove( apDn.size() - 1 );
1456 SubtreeSpecification ss = subentryCache.getSubentry( subentryDn ).getSubtreeSpecification();
1457 boolean isOldEntrySelected = evaluator.evaluate( ss, apDn, name, oldEntry );
1458 boolean isNewEntrySelected = evaluator.evaluate( ss, apDn, name, newEntry );
1459
1460 if ( isOldEntrySelected == isNewEntrySelected )
1461 {
1462 continue;
1463 }
1464
1465 // need to remove references to the subentry
1466 if ( isOldEntrySelected && !isNewEntrySelected )
1467 {
1468 for ( String aSUBENTRY_OPATTRS : SUBENTRY_OPATTRS )
1469 {
1470 ModificationOperation op = ModificationOperation.REPLACE_ATTRIBUTE;
1471 EntryAttribute opAttr = oldEntry.get( aSUBENTRY_OPATTRS );
1472
1473 if ( opAttr != null )
1474 {
1475 opAttr = opAttr.clone();
1476 opAttr.remove( subentryDn );
1477
1478 if ( opAttr.size() < 1 )
1479 {
1480 op = ModificationOperation.REMOVE_ATTRIBUTE;
1481 }
1482
1483 modList.add( new ServerModification( op, opAttr ) );
1484 }
1485 }
1486 }
1487 // need to add references to the subentry
1488 else if ( isNewEntrySelected && !isOldEntrySelected )
1489 {
1490 for ( String attribute : SUBENTRY_OPATTRS )
1491 {
1492 ModificationOperation op = ModificationOperation.ADD_ATTRIBUTE;
1493 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attribute );
1494 EntryAttribute opAttr = new DefaultServerAttribute( attribute, type );
1495 opAttr.add( subentryDn );
1496 modList.add( new ServerModification( op, opAttr ) );
1497 }
1498 }
1499 }
1500
1501 return modList;
1502 }
1503
1504 }