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