1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.operational;
21
22
23 import java.util.ArrayList;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Set;
28
29 import org.apache.directory.server.constants.ApacheSchemaConstants;
30 import org.apache.directory.server.constants.ServerDNConstants;
31 import org.apache.directory.server.core.DirectoryService;
32 import org.apache.directory.server.core.entry.ClonedServerEntry;
33 import org.apache.directory.server.core.entry.DefaultServerAttribute;
34 import org.apache.directory.server.core.entry.DefaultServerEntry;
35 import org.apache.directory.server.core.entry.ServerAttribute;
36 import org.apache.directory.server.core.entry.ServerEntry;
37 import org.apache.directory.server.core.entry.ServerModification;
38 import org.apache.directory.server.core.filtering.EntryFilter;
39 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
40 import org.apache.directory.server.core.interceptor.BaseInterceptor;
41 import org.apache.directory.server.core.interceptor.Interceptor;
42 import org.apache.directory.server.core.interceptor.NextInterceptor;
43 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
44 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
45 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
46 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
47 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
48 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
49 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
50 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
51 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
52 import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
53 import org.apache.directory.server.schema.registries.Registries;
54 import org.apache.directory.shared.ldap.constants.SchemaConstants;
55 import org.apache.directory.shared.ldap.entry.EntryAttribute;
56 import org.apache.directory.shared.ldap.entry.Modification;
57 import org.apache.directory.shared.ldap.entry.ModificationOperation;
58 import org.apache.directory.shared.ldap.entry.Value;
59 import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
60 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
61 import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
62 import org.apache.directory.shared.ldap.name.LdapDN;
63 import org.apache.directory.shared.ldap.name.Rdn;
64 import org.apache.directory.shared.ldap.schema.AttributeType;
65 import org.apache.directory.shared.ldap.schema.UsageEnum;
66 import org.apache.directory.shared.ldap.util.DateUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class OperationalAttributeInterceptor extends BaseInterceptor
83 {
84
85 private static Logger LOG = LoggerFactory.getLogger( OperationalAttributeInterceptor.class );
86
87 private final EntryFilter DENORMALIZING_SEARCH_FILTER = new EntryFilter()
88 {
89 public boolean accept( SearchingOperationContext operation, ClonedServerEntry serverEntry )
90 throws Exception
91 {
92 if ( operation.getSearchControls().getReturningAttributes() == null )
93 {
94 return true;
95 }
96
97 return filterDenormalized( serverEntry );
98 }
99 };
100
101
102
103
104 private final EntryFilter SEARCH_FILTER = new EntryFilter()
105 {
106 public boolean accept( SearchingOperationContext operation, ClonedServerEntry entry )
107 throws Exception
108 {
109 return operation.getSearchControls().getReturningAttributes() != null
110 || filterOperationalAttributes( entry );
111 }
112 };
113
114
115 private AttributeTypeRegistry atRegistry;
116
117 private DirectoryService service;
118
119 private LdapDN subschemaSubentryDn;
120
121
122 private Registries registries;
123
124 private static AttributeType CREATE_TIMESTAMP_ATTRIBUTE_TYPE;
125
126
127
128
129
130 public OperationalAttributeInterceptor()
131 {
132 }
133
134
135 public void init( DirectoryService directoryService ) throws Exception
136 {
137 service = directoryService;
138 registries = directoryService.getRegistries();
139 atRegistry = registries.getAttributeTypeRegistry();
140
141
142 Value<?> subschemaSubentry = service.getPartitionNexus()
143 .getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
144 subschemaSubentryDn = new LdapDN( (String)subschemaSubentry.get() );
145 subschemaSubentryDn.normalize( atRegistry.getNormalizerMapping() );
146
147 CREATE_TIMESTAMP_ATTRIBUTE_TYPE = atRegistry.lookup( SchemaConstants.CREATE_TIMESTAMP_AT );
148 }
149
150
151 public void destroy()
152 {
153 }
154
155
156
157
158
159 public void add( NextInterceptor nextInterceptor, AddOperationContext opContext )
160 throws Exception
161 {
162 String principal = getPrincipal().getName();
163
164 ServerEntry entry = opContext.getEntry();
165
166 entry.put( SchemaConstants.CREATORS_NAME_AT, principal );
167
168 EntryAttribute createTimeStamp = new DefaultServerAttribute( CREATE_TIMESTAMP_ATTRIBUTE_TYPE );
169
170 if ( opContext.getEntry().contains( createTimeStamp ) )
171 {
172
173
174 if ( opContext.getSession().getAuthenticatedPrincipal().getName().equals(
175 ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED ))
176 {
177 entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
178 }
179 else
180 {
181 String message = "The CreateTimeStamp attribute cannot be created by a user";
182 LOG.error( message );
183 throw new LdapSchemaViolationException( message, ResultCodeEnum.INSUFFICIENT_ACCESS_RIGHTS );
184 }
185 }
186 else
187 {
188 entry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
189 }
190
191 nextInterceptor.add( opContext );
192 }
193
194
195 public void modify( NextInterceptor nextInterceptor, ModifyOperationContext opContext )
196 throws Exception
197 {
198 nextInterceptor.modify( opContext );
199
200 if ( opContext.getDn().getNormName().equals( subschemaSubentryDn.getNormName() ) )
201 {
202 return;
203 }
204
205
206
207
208
209 List<Modification> modItemList = new ArrayList<Modification>(2);
210
211 AttributeType modifiersNameAt = atRegistry.lookup( SchemaConstants.MODIFIERS_NAME_AT );
212 ServerAttribute attribute = new DefaultServerAttribute(
213 SchemaConstants.MODIFIERS_NAME_AT,
214 modifiersNameAt,
215 getPrincipal().getName());
216
217 Modification modifiers = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
218
219 modItemList.add( modifiers );
220
221 AttributeType modifyTimeStampAt = atRegistry.lookup( SchemaConstants.MODIFY_TIMESTAMP_AT );
222 attribute = new DefaultServerAttribute(
223 SchemaConstants.MODIFY_TIMESTAMP_AT,
224 modifyTimeStampAt,
225 DateUtils.getGeneralizedTime() );
226
227 Modification timestamp = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attribute );
228
229 modItemList.add( timestamp );
230
231
232
233
234
235 ModifyOperationContext newModify = new ModifyOperationContext( opContext.getSession(),
236 opContext.getDn(), modItemList );
237 service.getPartitionNexus().modify( newModify );
238 }
239
240
241 public void rename( NextInterceptor nextInterceptor, RenameOperationContext opContext )
242 throws Exception
243 {
244 nextInterceptor.rename( opContext );
245
246
247 ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
248 serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
249 serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
250
251 LdapDN newDn = ( LdapDN ) opContext.getDn().clone();
252 newDn.remove( opContext.getDn().size() - 1 );
253 newDn.add( opContext.getNewRdn() );
254 newDn.normalize( atRegistry.getNormalizerMapping() );
255
256 List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
257
258 ModifyOperationContext newModify = new ModifyOperationContext( opContext.getSession(), newDn, items );
259
260 service.getPartitionNexus().modify( newModify );
261 }
262
263
264 public void move( NextInterceptor nextInterceptor, MoveOperationContext opContext ) throws Exception
265 {
266 nextInterceptor.move( opContext );
267
268
269 ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
270 serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
271 serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
272
273 List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
274
275
276 ModifyOperationContext newModify =
277 new ModifyOperationContext( opContext.getSession(), opContext.getParent(), items );
278
279 service.getPartitionNexus().modify( newModify );
280 }
281
282
283 public void moveAndRename( NextInterceptor nextInterceptor, MoveAndRenameOperationContext opContext )
284 throws Exception
285 {
286 nextInterceptor.moveAndRename( opContext );
287
288
289 ServerEntry serverEntry = new DefaultServerEntry( registries, opContext.getDn() );
290 serverEntry.put( SchemaConstants.MODIFIERS_NAME_AT, getPrincipal().getName() );
291 serverEntry.put( SchemaConstants.MODIFY_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
292
293 List<Modification> items = ModifyOperationContext.createModItems( serverEntry, ModificationOperation.REPLACE_ATTRIBUTE );
294
295 ModifyOperationContext newModify =
296 new ModifyOperationContext( opContext.getSession(), opContext.getParent(), items );
297
298 service.getPartitionNexus().modify( newModify );
299 }
300
301
302 public ClonedServerEntry lookup( NextInterceptor nextInterceptor, LookupOperationContext opContext ) throws Exception
303 {
304 ClonedServerEntry result = nextInterceptor.lookup( opContext );
305
306 if ( result == null )
307 {
308 return null;
309 }
310
311 if ( opContext.getAttrsId() == null )
312 {
313 filterOperationalAttributes( result );
314 }
315 else
316 {
317 filter( opContext, result );
318 }
319
320 return result;
321 }
322
323
324 public EntryFilteringCursor list( NextInterceptor nextInterceptor, ListOperationContext opContext ) throws Exception
325 {
326 EntryFilteringCursor cursor = nextInterceptor.list( opContext );
327 cursor.addEntryFilter( SEARCH_FILTER );
328 return cursor;
329 }
330
331
332 public EntryFilteringCursor search( NextInterceptor nextInterceptor, SearchOperationContext opContext ) throws Exception
333 {
334 EntryFilteringCursor cursor = nextInterceptor.search( opContext );
335
336 if ( opContext.isAllOperationalAttributes() ||
337 ( opContext.getReturningAttributes() != null && ! opContext.getReturningAttributes().isEmpty() ) )
338 {
339 if ( service.isDenormalizeOpAttrsEnabled() )
340 {
341 cursor.addEntryFilter( DENORMALIZING_SEARCH_FILTER );
342 }
343
344 return cursor;
345 }
346
347 cursor.addEntryFilter( SEARCH_FILTER );
348 return cursor;
349 }
350
351
352
353
354
355
356
357
358
359
360 private boolean filterOperationalAttributes( ServerEntry attributes ) throws Exception
361 {
362 Set<AttributeType> removedAttributes = new HashSet<AttributeType>();
363
364
365 for ( AttributeType attributeType:attributes.getAttributeTypes() )
366 {
367 if ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS )
368 {
369 removedAttributes.add( attributeType );
370 }
371 }
372
373
374 for ( AttributeType attributeType:removedAttributes )
375 {
376 attributes.removeAttributes( attributeType );
377 }
378
379 return true;
380 }
381
382
383 private void filter( LookupOperationContext lookupContext, ServerEntry entry ) throws Exception
384 {
385 LdapDN dn = lookupContext.getDn();
386 List<String> ids = lookupContext.getAttrsId();
387
388
389 if ( ids == null || ids.isEmpty() )
390 {
391 filterOperationalAttributes( entry );
392 return;
393 }
394
395 Set<AttributeType> attributeTypes = entry.getAttributeTypes();
396
397 if ( dn.size() == 0 )
398 {
399 for ( AttributeType attributeType:attributeTypes )
400 {
401 if ( !ids.contains( attributeType.getOid() ) )
402 {
403 entry.removeAttributes( attributeType );
404 }
405 }
406 }
407
408 denormalizeEntryOpAttrs( entry );
409
410
411
412
413 }
414
415
416 public void denormalizeEntryOpAttrs( ServerEntry entry ) throws Exception
417 {
418 if ( service.isDenormalizeOpAttrsEnabled() )
419 {
420 EntryAttribute attr = entry.get( SchemaConstants.CREATORS_NAME_AT );
421
422 if ( attr != null )
423 {
424 LdapDN creatorsName = new LdapDN( attr.getString() );
425
426 attr.clear();
427 attr.add( denormalizeTypes( creatorsName ).getUpName() );
428 }
429
430 attr = entry.get( SchemaConstants.MODIFIERS_NAME_AT );
431
432 if ( attr != null )
433 {
434 LdapDN modifiersName = new LdapDN( attr.getString() );
435
436 attr.clear();
437 attr.add( denormalizeTypes( modifiersName ).getUpName() );
438 }
439
440 attr = entry.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT );
441
442 if ( attr != null )
443 {
444 LdapDN modifiersName = new LdapDN( attr.getString() );
445
446 attr.clear();
447 attr.add( denormalizeTypes( modifiersName ).getUpName() );
448 }
449 }
450 }
451
452
453
454
455
456
457
458
459
460
461 public LdapDN denormalizeTypes( LdapDN dn ) throws Exception
462 {
463 LdapDN newDn = new LdapDN();
464
465 for ( int ii = 0; ii < dn.size(); ii++ )
466 {
467 Rdn rdn = dn.getRdn( ii );
468 if ( rdn.size() == 0 )
469 {
470 newDn.add( new Rdn() );
471 continue;
472 }
473 else if ( rdn.size() == 1 )
474 {
475 String name = atRegistry.lookup( rdn.getNormType() ).getName();
476 String value = (String)rdn.getAtav().getNormValue();
477 newDn.add( new Rdn( name, name, value, value ) );
478 continue;
479 }
480
481
482 StringBuffer buf = new StringBuffer();
483
484 for ( Iterator<AttributeTypeAndValue> atavs = rdn.iterator(); atavs.hasNext();
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508