View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.operational;
18  
19  
20  import org.apache.ldap.common.schema.AttributeType;
21  import org.apache.ldap.common.schema.UsageEnum;
22  import org.apache.ldap.common.util.DateUtils;
23  import org.apache.ldap.server.RootNexus;
24  import org.apache.ldap.server.interceptor.BaseInterceptor;
25  import org.apache.ldap.server.interceptor.InterceptorContext;
26  import org.apache.ldap.server.interceptor.NextInterceptor;
27  import org.apache.ldap.server.db.ResultFilteringEnumeration;
28  import org.apache.ldap.server.db.SearchResultFilter;
29  import org.apache.ldap.server.invocation.*;
30  import org.apache.ldap.server.schema.AttributeTypeRegistry;
31  
32  import javax.naming.Name;
33  import javax.naming.NamingEnumeration;
34  import javax.naming.NamingException;
35  import javax.naming.directory.*;
36  import javax.naming.ldap.LdapContext;
37  import java.util.HashSet;
38  
39  
40  /***
41   * An {@link org.apache.ldap.server.interceptor.Interceptor} that adds or modifies the default attributes
42   * of entries. There are four default attributes for now;<code>'creatorsName'
43   * </code>, <code>'createTimestamp'</code>, <code>'modifiersName'</code>, and
44   * <code>'modifyTimestamp'</code>.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   * @version $Rev: 159266 $, $Date: 2005-03-28 12:30:19 -0500 (Mon, 28 Mar 2005) $
48   */
49  public class OperationalAttributeService extends BaseInterceptor
50  {
51      /***
52       * the database search result filter to register with filter service
53       */
54      private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
55      {
56          public boolean accept( LdapContext ctx, SearchResult result, SearchControls controls )
57                  throws NamingException
58          {
59              if ( controls.getReturningAttributes() == null )
60              {
61                  return filter( result.getAttributes() );
62              }
63  
64              return true;
65          }
66      };
67  
68      /***
69       * the root nexus of the system
70       */
71      private RootNexus nexus;
72  
73      private AttributeTypeRegistry registry;
74  
75  
76      /***
77       * Creates the operational attribute management service interceptor.
78       */
79      public OperationalAttributeService()
80      {
81      }
82  
83  
84      public void init( InterceptorContext ctx ) throws NamingException
85      {
86          nexus = ctx.getRootNexus();
87          registry = ctx.getGlobalRegistries().getAttributeTypeRegistry();
88      }
89  
90  
91      public void destroy()
92      {
93      }
94  
95  
96      /***
97       * Adds extra operational attributes to the entry before it is added.
98       */
99      protected void process( NextInterceptor nextInterceptor, Add call ) throws NamingException
100     {
101         String principal = getPrincipal( call ).getName();
102         Attributes entry = call.getAttributes();
103 
104         BasicAttribute attribute = new BasicAttribute( "creatorsName" );
105         attribute.add( principal );
106         entry.put( attribute );
107 
108         attribute = new BasicAttribute( "createTimestamp" );
109         attribute.add( DateUtils.getGeneralizedTime() );
110         entry.put( attribute );
111 
112         nextInterceptor.process( call );
113     }
114 
115 
116     protected void process( NextInterceptor nextInterceptor, Modify call ) throws NamingException
117     {
118         nextInterceptor.process( call );
119         
120         // add operational attributes after call in case the operation fails
121         Attributes attributes = new BasicAttributes();
122         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
123         attribute.add( getPrincipal( call ).getName() );
124         attributes.put( attribute );
125 
126         attribute = new BasicAttribute( "modifyTimestamp" );
127         attribute.add( DateUtils.getGeneralizedTime() );
128         attributes.put( attribute );
129 
130         nexus.modify( call.getName(), DirContext.REPLACE_ATTRIBUTE, attributes );
131     }
132 
133 
134     protected void process( NextInterceptor nextInterceptor, ModifyMany call ) throws NamingException
135     {
136         nextInterceptor.process( call );
137 
138         // add operational attributes after call in case the operation fails
139         Attributes attributes = new BasicAttributes();
140         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
141         attribute.add( getPrincipal( call ).getName() );
142         attributes.put( attribute );
143 
144         attribute = new BasicAttribute( "modifyTimestamp" );
145         attribute.add( DateUtils.getGeneralizedTime() );
146         attributes.put( attribute );
147 
148         nexus.modify( call.getName(), DirContext.REPLACE_ATTRIBUTE, attributes );
149     }
150 
151 
152     protected void process( NextInterceptor nextInterceptor, ModifyRN call ) throws NamingException
153     {
154         nextInterceptor.process( call );
155         
156         // add operational attributes after call in case the operation fails
157         Attributes attributes = new BasicAttributes();
158         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
159         attribute.add( getPrincipal( call ).getName() );
160         attributes.put( attribute );
161 
162         attribute = new BasicAttribute( "modifyTimestamp" );
163         attribute.add( DateUtils.getGeneralizedTime() );
164         attributes.put( attribute );
165 
166         Name newDn = call.getName().getSuffix( 1 ).add( call.getNewRelativeName() );
167         nexus.modify( newDn, DirContext.REPLACE_ATTRIBUTE, attributes );
168     }
169 
170 
171     protected void process( NextInterceptor nextInterceptor, Move call ) throws NamingException
172     {
173         nextInterceptor.process( call );
174 
175         // add operational attributes after call in case the operation fails
176         Attributes attributes = new BasicAttributes();
177         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
178         attribute.add( getPrincipal( call ).getName() );
179         attributes.put( attribute );
180 
181         attribute = new BasicAttribute( "modifyTimestamp" );
182         attribute.add( DateUtils.getGeneralizedTime() );
183         attributes.put( attribute );
184 
185         nexus.modify( call.getNewParentName(), DirContext.REPLACE_ATTRIBUTE, attributes );
186     }
187 
188 
189     protected void process( NextInterceptor nextInterceptor, MoveAndModifyRN call ) throws NamingException
190     {
191         nextInterceptor.process( call );
192 
193         // add operational attributes after call in case the operation fails
194         Attributes attributes = new BasicAttributes();
195         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
196         attribute.add( getPrincipal( call ).getName() );
197         attributes.put( attribute );
198 
199         attribute = new BasicAttribute( "modifyTimestamp" );
200         attribute.add( DateUtils.getGeneralizedTime() );
201         attributes.put( attribute );
202 
203         nexus.modify( call.getNewParentName(), DirContext.REPLACE_ATTRIBUTE, attributes );
204     }
205 
206 
207     protected void process( NextInterceptor nextInterceptor, Lookup call ) throws NamingException
208     {
209         nextInterceptor.process( call );
210 
211         Attributes attributes = ( Attributes ) call.getReturnValue();
212         Attributes retval = ( Attributes ) attributes.clone();
213         filter( retval );
214         call.setReturnValue( retval );
215     }
216 
217 
218     protected void process( NextInterceptor nextInterceptor, LookupWithAttrIds call ) throws NamingException
219     {
220         nextInterceptor.process( call );
221 
222         Attributes attributes = ( Attributes ) call.getReturnValue();
223         if ( attributes == null )
224         {
225             return;
226         }
227 
228         Attributes retval = ( Attributes ) attributes.clone();
229         filter( call.getName(), retval, call.getAttributeIds() );
230         call.setReturnValue( retval );
231     }
232 
233 
234     protected void process( NextInterceptor nextInterceptor, List call ) throws NamingException
235     {
236         nextInterceptor.process( call );
237 
238         NamingEnumeration e;
239         ResultFilteringEnumeration retval;
240         LdapContext ctx = ( LdapContext ) call.getContextStack().peek();
241         e = ( NamingEnumeration ) call.getReturnValue();
242         retval = new ResultFilteringEnumeration( e, new SearchControls(), ctx, SEARCH_FILTER );
243         call.setReturnValue( retval );
244     }
245 
246 
247     protected void process( NextInterceptor nextInterceptor, Search call ) throws NamingException
248     {
249         nextInterceptor.process( call );
250 
251         SearchControls searchControls = call.getControls();
252         if ( searchControls.getReturningAttributes() != null )
253         {
254             return;
255         }
256 
257         NamingEnumeration e;
258         ResultFilteringEnumeration retval;
259         LdapContext ctx = ( LdapContext ) call.getContextStack().peek();
260         e = ( NamingEnumeration ) call.getReturnValue();
261         retval = new ResultFilteringEnumeration( e, searchControls, ctx, SEARCH_FILTER );
262         call.setReturnValue( retval );
263     }
264 
265 
266     /***
267      * Filters out the operational attributes within a search results attributes.  The attributes are directly
268      * modified.
269      *
270      * @param attributes the resultant attributes to filter
271      * @return true always
272      */
273     private boolean filter( Attributes attributes ) throws NamingException
274     {
275         NamingEnumeration list = attributes.getIDs();
276 
277         while ( list.hasMore() )
278         {
279             String attrId = ( String ) list.next();
280 
281             AttributeType type = null;
282 
283             if ( registry.hasAttributeType( attrId ) )
284             {
285                 type = registry.lookup( attrId );
286             }
287 
288             if ( type != null && type.getUsage() != UsageEnum.USERAPPLICATIONS )
289             {
290                 attributes.remove( attrId );
291             }
292         }
293         return true;
294     }
295 
296 
297     private void filter( Name dn, Attributes entry, String[] ids )
298             throws NamingException
299     {
300         // still need to protect against returning op attrs when ids is null
301         if ( ids == null )
302         {
303             OperationalAttributeService.this.filter( entry );
304             return;
305         }
306 
307         if ( dn.size() == 0 )
308         {
309             HashSet idsSet = new HashSet( ids.length );
310 
311             for ( int ii = 0; ii < ids.length; ii++ )
312             {
313                 idsSet.add( ids[ii].toLowerCase() );
314             }
315 
316             NamingEnumeration list = entry.getIDs();
317 
318             while ( list.hasMore() )
319             {
320                 String attrId = ( ( String ) list.nextElement() ).toLowerCase();
321 
322                 if ( !idsSet.contains( attrId ) )
323                 {
324                     entry.remove( attrId );
325                 }
326             }
327         }
328         
329         // do nothing past here since this explicity specifies which
330         // attributes to include - backends will automatically populate
331         // with right set of attributes using ids array
332     }
333 }