1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.schema;
18
19
20 import org.apache.ldap.common.filter.ExprNode;
21 import org.apache.ldap.common.filter.PresenceNode;
22 import org.apache.ldap.common.filter.SimpleNode;
23 import org.apache.ldap.common.message.LockableAttributeImpl;
24 import org.apache.ldap.common.message.LockableAttributesImpl;
25 import org.apache.ldap.common.name.LdapName;
26 import org.apache.ldap.common.schema.*;
27 import org.apache.ldap.common.util.SingletonEnumeration;
28 import org.apache.ldap.server.RootNexus;
29 import org.apache.ldap.server.interceptor.BaseInterceptor;
30 import org.apache.ldap.server.interceptor.InterceptorContext;
31 import org.apache.ldap.server.interceptor.NextInterceptor;
32 import org.apache.ldap.server.db.ResultFilteringEnumeration;
33 import org.apache.ldap.server.db.SearchResultFilter;
34 import org.apache.ldap.server.invocation.List;
35 import org.apache.ldap.server.invocation.Lookup;
36 import org.apache.ldap.server.invocation.LookupWithAttrIds;
37 import org.apache.ldap.server.invocation.Search;
38 import org.apache.ldap.server.jndi.ServerLdapContext;
39 import org.apache.ldap.server.schema.AttributeTypeRegistry;
40 import org.apache.ldap.server.schema.GlobalRegistries;
41
42 import javax.naming.NamingEnumeration;
43 import javax.naming.NamingException;
44 import javax.naming.directory.Attribute;
45 import javax.naming.directory.Attributes;
46 import javax.naming.directory.SearchControls;
47 import javax.naming.directory.SearchResult;
48 import javax.naming.ldap.LdapContext;
49 import java.util.Collections;
50 import java.util.HashSet;
51 import java.util.Iterator;
52 import java.util.Set;
53
54
55 /***
56 * An {@link org.apache.ldap.server.interceptor.Interceptor} that manages and enforces schemas.
57 *
58 * @todo Better interceptor description required.
59 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60 * @version $Rev: 159262 $, $Date: 2005-03-28 12:18:38 -0500 (Mon, 28 Mar 2005) $
61 */
62 public class SchemaService extends BaseInterceptor
63 {
64 private static final String BINARY_KEY = "java.naming.ldap.attributes.binary";
65
66 /***
67 * the root nexus to all database partitions
68 */
69 private RootNexus nexus;
70
71 /***
72 * a binary attribute tranforming filter: String -> byte[]
73 */
74 private BinaryAttributeFilter binaryAttributeFilter;
75
76 /***
77 * the global schema object registries
78 */
79 private GlobalRegistries globalRegistries;
80
81 private AttributeTypeRegistry attributeRegistry;
82
83 /***
84 * subschemaSubentry attribute's value from Root DSE
85 */
86 private String subentryDn;
87
88
89 /***
90 * Creates a schema service interceptor.
91 */
92 public SchemaService()
93 {
94 }
95
96
97 public void init( InterceptorContext ctx ) throws NamingException
98 {
99 this.nexus = ctx.getRootNexus();
100 this.globalRegistries = ctx.getGlobalRegistries();
101 attributeRegistry = globalRegistries.getAttributeTypeRegistry();
102 binaryAttributeFilter = new BinaryAttributeFilter();
103
104
105 String subschemaSubentry = ( String ) nexus.getRootDSE().get( "subschemaSubentry" ).get();
106 subentryDn = new LdapName( subschemaSubentry ).toString().toLowerCase();
107 }
108
109
110 public void destroy()
111 {
112 }
113
114
115 protected void process( NextInterceptor nextInterceptor, List call ) throws NamingException
116 {
117 nextInterceptor.process( call );
118
119 NamingEnumeration e;
120 ResultFilteringEnumeration retval;
121 LdapContext ctx = ( LdapContext ) call.getContextStack().peek();
122 e = ( NamingEnumeration ) call.getReturnValue();
123 retval = new ResultFilteringEnumeration( e, new SearchControls(), ctx, binaryAttributeFilter );
124 call.setReturnValue( retval );
125 }
126
127
128 protected void process( NextInterceptor nextInterceptor, Search call ) throws NamingException
129 {
130
131 if ( !subentryDn.equals( call.getBaseName().toString() ) )
132 {
133 nextInterceptor.process( call );
134 return;
135 }
136
137 boolean bypass = false;
138 SearchControls searchControls = call.getControls();
139 ExprNode filter = call.getFilter();
140 if ( searchControls.getSearchScope() == SearchControls.OBJECT_SCOPE &&
141 filter instanceof SimpleNode )
142 {
143 SimpleNode node = ( SimpleNode ) filter;
144
145 if ( node.getAttribute().equalsIgnoreCase( "objectClass" ) &&
146 node.getValue().equalsIgnoreCase( "subschema" ) &&
147 node.getAssertionType() == SimpleNode.EQUALITY
148 )
149 {
150
151 Attributes attrs = getSubschemaEntry( searchControls.getReturningAttributes() );
152 SearchResult result = new SearchResult( call.getBaseName().toString(), null, attrs );
153 SingletonEnumeration e = new SingletonEnumeration( result );
154 call.setReturnValue( e );
155 bypass = true;
156 }
157 }
158 else if ( searchControls.getSearchScope() == SearchControls.OBJECT_SCOPE &&
159 filter instanceof PresenceNode )
160 {
161 PresenceNode node = ( PresenceNode ) filter;
162
163 if ( node.getAttribute().equalsIgnoreCase( "objectClass" ) )
164 {
165
166 Attributes attrs = getSubschemaEntry( searchControls.getReturningAttributes() );
167 SearchResult result = new SearchResult( call.getBaseName().toString(), null, attrs );
168 SingletonEnumeration e = new SingletonEnumeration( result );
169 call.setReturnValue( e );
170 bypass = true;
171 }
172 }
173
174 if ( !bypass )
175 {
176 nextInterceptor.process( call );
177 }
178
179 if ( searchControls.getReturningAttributes() != null )
180 {
181 return;
182 }
183
184 NamingEnumeration e;
185 ResultFilteringEnumeration retval;
186 LdapContext ctx = ( LdapContext ) call.getContextStack().peek();
187 e = ( NamingEnumeration ) call.getReturnValue();
188 retval = new ResultFilteringEnumeration( e, searchControls, ctx, binaryAttributeFilter );
189 call.setReturnValue( retval );
190 }
191
192
193 private Attributes getSubschemaEntry( String[] ids ) throws NamingException
194 {
195 if ( ids == null )
196 {
197 return new LockableAttributesImpl();
198 }
199
200 HashSet set = new HashSet( ids.length );
201 LockableAttributesImpl attrs = new LockableAttributesImpl();
202 LockableAttributeImpl attr = null;
203
204 for ( int ii = 0; ii < ids.length; ii++ )
205 {
206 set.add( ids[ii].toLowerCase() );
207 }
208
209
210 if ( set.contains( "objectclasses" ) )
211 {
212 attr = new LockableAttributeImpl( attrs, "objectClasses" );
213 Iterator list = globalRegistries.getObjectClassRegistry().list();
214 while ( list.hasNext() )
215 {
216 ObjectClass oc = ( ObjectClass ) list.next();
217 attr.add( SchemaUtils.render( oc ).toString() );
218 }
219 attrs.put( attr );
220 }
221
222 if ( set.contains( "attributetypes" ) )
223 {
224 attr = new LockableAttributeImpl( attrs, "attributeTypes" );
225 Iterator list = globalRegistries.getAttributeTypeRegistry().list();
226 while ( list.hasNext() )
227 {
228 AttributeType at = ( AttributeType ) list.next();
229 attr.add( SchemaUtils.render( at ).toString() );
230 }
231 attrs.put( attr );
232 }
233
234 if ( set.contains( "matchingrules" ) )
235 {
236 attr = new LockableAttributeImpl( attrs, "matchingRules" );
237 Iterator list = globalRegistries.getMatchingRuleRegistry().list();
238 while ( list.hasNext() )
239 {
240 MatchingRule mr = ( MatchingRule ) list.next();
241 attr.add( SchemaUtils.render( mr ).toString() );
242 }
243 attrs.put( attr );
244 }
245
246 if ( set.contains( "matchingruleuse" ) )
247 {
248 attr = new LockableAttributeImpl( attrs, "matchingRuleUse" );
249 Iterator list = globalRegistries.getMatchingRuleUseRegistry().list();
250 while ( list.hasNext() )
251 {
252 MatchingRuleUse mru = ( MatchingRuleUse ) list.next();
253 attr.add( SchemaUtils.render( mru ).toString() );
254 }
255 attrs.put( attr );
256 }
257
258 if ( set.contains( "ldapsyntaxes" ) )
259 {
260 attr = new LockableAttributeImpl( attrs, "ldapSyntaxes" );
261 Iterator list = globalRegistries.getSyntaxRegistry().list();
262 while ( list.hasNext() )
263 {
264 Syntax syntax = ( Syntax ) list.next();
265 attr.add( SchemaUtils.render( syntax ).toString() );
266 }
267 attrs.put( attr );
268 }
269
270 if ( set.contains( "ditcontentrules" ) )
271 {
272 attr = new LockableAttributeImpl( attrs, "dITContentRules" );
273 Iterator list = globalRegistries.getDitContentRuleRegistry().list();
274 while ( list.hasNext() )
275 {
276 DITContentRule dcr = ( DITContentRule ) list.next();
277 attr.add( SchemaUtils.render( dcr ).toString() );
278 }
279 attrs.put( attr );
280 }
281
282 if ( set.contains( "ditstructurerules" ) )
283 {
284 attr = new LockableAttributeImpl( attrs, "dITStructureRules" );
285 Iterator list = globalRegistries.getDitStructureRuleRegistry().list();
286 while ( list.hasNext() )
287 {
288 DITStructureRule dsr = ( DITStructureRule ) list.next();
289 attr.add( SchemaUtils.render( dsr ).toString() );
290 }
291 attrs.put( attr );
292 }
293
294 if ( set.contains( "nameforms" ) )
295 {
296 attr = new LockableAttributeImpl( attrs, "nameForms" );
297 Iterator list = globalRegistries.getNameFormRegistry().list();
298 while ( list.hasNext() )
299 {
300 NameForm nf = ( NameForm ) list.next();
301 attr.add( SchemaUtils.render( nf ).toString() );
302 }
303 attrs.put( attr );
304 }
305
306
307 attr = new LockableAttributeImpl( attrs, "objectClass" );
308 attr.add( "top" );
309 attr.add( "subschema" );
310 attrs.put( attr );
311
312
313 attrs.put( "cn", "schema" );
314
315 return attrs;
316 }
317
318
319 protected void process( NextInterceptor nextInterceptor, Lookup call ) throws NamingException
320 {
321 nextInterceptor.process( call );
322
323 ServerLdapContext ctx = ( ServerLdapContext ) call.getContextStack().peek();
324 Attributes attributes = ( Attributes ) call.getReturnValue();
325 Attributes retval = ( Attributes ) attributes.clone();
326 doFilter( ctx, retval );
327 call.setReturnValue( retval );
328 }
329
330
331 protected void process( NextInterceptor nextInterceptor, LookupWithAttrIds call ) throws NamingException
332 {
333 nextInterceptor.process( call );
334
335 ServerLdapContext ctx = ( ServerLdapContext ) call.getContextStack().peek();
336 Attributes attributes = ( Attributes ) call.getReturnValue();
337 if ( attributes == null )
338 {
339 return;
340 }
341
342 Attributes retval = ( Attributes ) attributes.clone();
343 doFilter( ctx, retval );
344 call.setReturnValue( retval );
345 }
346
347
348 private void doFilter( LdapContext ctx, Attributes entry )
349 throws NamingException
350 {
351
352 Set binaries;
353
354
355 String binaryIds = ( String ) ctx.getEnvironment().get( BINARY_KEY );
356
357 if ( binaryIds == null )
358 {
359 binaries = Collections.EMPTY_SET;
360 }
361 else
362 {
363 String[] binaryArray = binaryIds.split( " " );
364
365 binaries = new HashSet( binaryArray.length );
366
367 for ( int ii = 0; ii < binaryArray.length; ii++ )
368 {
369 AttributeType type = attributeRegistry.lookup( binaryArray[ii] );
370
371 binaries.add( type );
372 }
373 }
374
375
376
377
378
379 NamingEnumeration list = entry.getIDs();
380
381 while ( list.hasMore() )
382 {
383 String id = ( String ) list.next();
384
385 AttributeType type = null;
386
387 boolean asBinary = false;
388
389 if ( attributeRegistry.hasAttributeType( id ) )
390 {
391 type = attributeRegistry.lookup( id );
392 }
393
394 if ( type != null )
395 {
396 asBinary = !type.getSyntax().isHumanReadible();
397
398 asBinary = asBinary || binaries.contains( type );
399 }
400
401 if ( asBinary )
402 {
403 Attribute attribute = entry.get( id );
404
405 Attribute binary = new LockableAttributeImpl( id );
406
407 for ( int ii = 0; ii < attribute.size(); ii++ )
408 {
409 Object value = attribute.get( ii );
410
411 if ( value instanceof String )
412 {
413 binary.add( ii, ( ( String ) value ).getBytes() );
414 }
415 else
416 {
417 binary.add( ii, value );
418 }
419 }
420
421 entry.remove( id );
422
423 entry.put( binary );
424 }
425 }
426 }
427
428
429 /***
430 * A special filter over entry attributes which replaces Attribute String values with their respective byte[]
431 * representations using schema information and the value held in the JNDI environment property:
432 * <code>java.naming.ldap.attributes.binary</code>.
433 *
434 * @see <a href= "http://java.sun.com/j2se/1.4.2/docs/guide/jndi/jndi-ldap-gl.html#binary">
435 * java.naming.ldap.attributes.binary</a>
436 */
437 private class BinaryAttributeFilter implements SearchResultFilter
438 {
439 public BinaryAttributeFilter()
440 {
441 }
442
443
444 public boolean accept( LdapContext ctx, SearchResult result, SearchControls controls ) throws NamingException
445 {
446 doFilter( ctx, result.getAttributes() );
447 return true;
448 }
449 }
450 }