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 java.util.HashSet;
21  import java.util.Map;
22  
23  import javax.naming.Name;
24  import javax.naming.NamingEnumeration;
25  import javax.naming.NamingException;
26  import javax.naming.directory.Attributes;
27  import javax.naming.directory.BasicAttribute;
28  import javax.naming.directory.BasicAttributes;
29  import javax.naming.directory.DirContext;
30  import javax.naming.directory.ModificationItem;
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.schema.AttributeType;
37  import org.apache.ldap.common.schema.UsageEnum;
38  import org.apache.ldap.common.util.DateUtils;
39  import org.apache.ldap.server.configuration.InterceptorConfiguration;
40  import org.apache.ldap.server.enumeration.SearchResultFilter;
41  import org.apache.ldap.server.enumeration.SearchResultFilteringEnumeration;
42  import org.apache.ldap.server.interceptor.BaseInterceptor;
43  import org.apache.ldap.server.interceptor.Interceptor;
44  import org.apache.ldap.server.interceptor.NextInterceptor;
45  import org.apache.ldap.server.invocation.InvocationStack;
46  import org.apache.ldap.server.jndi.ContextFactoryConfiguration;
47  import org.apache.ldap.server.partition.ContextPartitionNexus;
48  import org.apache.ldap.server.schema.AttributeTypeRegistry;
49  
50  
51  /***
52   * An {@link Interceptor} that adds or modifies the default attributes
53   * of entries. There are four default attributes for now;
54   * <tt>'creatorsName'</tt>, <tt>'createTimestamp'</tt>, <tt>'modifiersName'</tt>,
55   * and <tt>'modifyTimestamp'</tt>.
56   *
57   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
58   * @version $Rev: 264732 $, $Date: 2005-08-30 04:04:51 -0400 (Tue, 30 Aug 2005) $
59   */
60  public class OperationalAttributeService extends BaseInterceptor
61  {
62      /***
63       * the database search result filter to register with filter service
64       */
65      private final SearchResultFilter SEARCH_FILTER = new SearchResultFilter()
66      {
67          public boolean accept( LdapContext ctx, SearchResult result, SearchControls controls )
68                  throws NamingException
69          {
70              if ( controls.getReturningAttributes() == null )
71              {
72                  return filter( result.getAttributes() );
73              }
74  
75              return true;
76          }
77      };
78  
79      /***
80       * the root nexus of the system
81       */
82      private ContextPartitionNexus nexus;
83  
84      private AttributeTypeRegistry registry;
85  
86  
87      /***
88       * Creates the operational attribute management service interceptor.
89       */
90      public OperationalAttributeService()
91      {
92      }
93  
94  
95      public void init( ContextFactoryConfiguration factoryCfg, InterceptorConfiguration cfg ) throws NamingException
96      {
97          nexus = factoryCfg.getPartitionNexus();
98          registry = factoryCfg.getGlobalRegistries().getAttributeTypeRegistry();
99      }
100 
101 
102     public void destroy()
103     {
104     }
105 
106 
107     /***
108      * Adds extra operational attributes to the entry before it is added.
109      */
110     public void add( NextInterceptor nextInterceptor, String upName, Name normName, Attributes entry ) throws NamingException
111     {
112         String principal = getPrincipal().getName();
113 
114         BasicAttribute attribute = new BasicAttribute( "creatorsName" );
115         attribute.add( principal );
116         entry.put( attribute );
117 
118         attribute = new BasicAttribute( "createTimestamp" );
119         attribute.add( DateUtils.getGeneralizedTime() );
120         entry.put( attribute );
121 
122         nextInterceptor.add( upName, normName, entry );
123     }
124 
125 
126     public void modify( NextInterceptor nextInterceptor, Name name, int modOp, Attributes attrs) throws NamingException
127     {
128         nextInterceptor.modify( name, modOp, attrs );
129         
130         // add operational attributes after call in case the operation fails
131         Attributes attributes = new BasicAttributes( true );
132         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
133         attribute.add( getPrincipal().getName() );
134         attributes.put( attribute );
135 
136         attribute = new BasicAttribute( "modifyTimestamp" );
137         attribute.add( DateUtils.getGeneralizedTime() );
138         attributes.put( attribute );
139 
140         nexus.modify( name, DirContext.REPLACE_ATTRIBUTE, attributes );
141     }
142 
143 
144     public void modify( NextInterceptor nextInterceptor, Name name, ModificationItem[] items ) throws NamingException
145     {
146         nextInterceptor.modify( name, items );
147 
148         // add operational attributes after call in case the operation fails
149         Attributes attributes = new BasicAttributes( true );
150         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
151         attribute.add( getPrincipal().getName() );
152         attributes.put( attribute );
153 
154         attribute = new BasicAttribute( "modifyTimestamp" );
155         attribute.add( DateUtils.getGeneralizedTime() );
156         attributes.put( attribute );
157 
158         nexus.modify( name, DirContext.REPLACE_ATTRIBUTE, attributes );
159     }
160 
161 
162     public void modifyRn( NextInterceptor nextInterceptor, Name name, String newRn, boolean deleteOldRn ) throws NamingException
163     {
164         nextInterceptor.modifyRn( name, newRn, deleteOldRn );
165         
166         // add operational attributes after call in case the operation fails
167         Attributes attributes = new BasicAttributes( true );
168         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
169         attribute.add( getPrincipal().getName() );
170         attributes.put( attribute );
171 
172         attribute = new BasicAttribute( "modifyTimestamp" );
173         attribute.add( DateUtils.getGeneralizedTime() );
174         attributes.put( attribute );
175 
176         Name newDn = name.getSuffix( 1 ).add( newRn );
177         nexus.modify( newDn, DirContext.REPLACE_ATTRIBUTE, attributes );
178     }
179 
180 
181     public void move( NextInterceptor nextInterceptor, Name name, Name newParentName ) throws NamingException
182     {
183         nextInterceptor.move( name, newParentName );
184 
185         // add operational attributes after call in case the operation fails
186         Attributes attributes = new BasicAttributes( true );
187         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
188         attribute.add( getPrincipal().getName() );
189         attributes.put( attribute );
190 
191         attribute = new BasicAttribute( "modifyTimestamp" );
192         attribute.add( DateUtils.getGeneralizedTime() );
193         attributes.put( attribute );
194 
195         nexus.modify( newParentName, DirContext.REPLACE_ATTRIBUTE, attributes );
196     }
197 
198 
199     public void move( NextInterceptor nextInterceptor, Name name, Name newParentName, String newRn, boolean deleteOldRn ) throws NamingException
200     {
201         nextInterceptor.move( name, newParentName, newRn, deleteOldRn );
202 
203         // add operational attributes after call in case the operation fails
204         Attributes attributes = new BasicAttributes( true );
205         BasicAttribute attribute = new BasicAttribute( "modifiersName" );
206         attribute.add( getPrincipal().getName() );
207         attributes.put( attribute );
208 
209         attribute = new BasicAttribute( "modifyTimestamp" );
210         attribute.add( DateUtils.getGeneralizedTime() );
211         attributes.put( attribute );
212 
213         nexus.modify( newParentName, DirContext.REPLACE_ATTRIBUTE, attributes );
214     }
215 
216 
217     public Attributes lookup( NextInterceptor nextInterceptor, Name name ) throws NamingException
218     {
219         Attributes result = nextInterceptor.lookup( name );
220         if ( result == null )
221         {
222             return null;
223         }
224         filter( result );
225         return result;
226     }
227 
228 
229     public Attributes lookup( NextInterceptor nextInterceptor, Name name, String[] attrIds ) throws NamingException
230     {
231         Attributes result = nextInterceptor.lookup( name, attrIds );
232         if ( result == null )
233         {
234             return null;
235         }
236 
237         filter( name, result, attrIds );
238         return result;
239     }
240 
241 
242     public NamingEnumeration list( NextInterceptor nextInterceptor, Name base ) throws NamingException
243     {
244         NamingEnumeration e = nextInterceptor.list( base );
245         LdapContext ctx =
246             ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
247         return new SearchResultFilteringEnumeration( e, new SearchControls(), ctx, SEARCH_FILTER );
248     }
249 
250 
251     public NamingEnumeration search( NextInterceptor nextInterceptor, 
252             Name base, Map env, ExprNode filter,
253             SearchControls searchCtls ) throws NamingException
254     {
255         NamingEnumeration e = nextInterceptor.search( base, env, filter, searchCtls );
256         if ( searchCtls.getReturningAttributes() != null )
257         {
258             return e;
259         }
260 
261         LdapContext ctx =
262             ( LdapContext ) InvocationStack.getInstance().peek().getCaller();
263         return new SearchResultFilteringEnumeration( e, searchCtls, ctx, SEARCH_FILTER );
264     }
265 
266 
267     /***
268      * Filters out the operational attributes within a search results attributes.  The attributes are directly
269      * modified.
270      *
271      * @param attributes the resultant attributes to filter
272      * @return true always
273      */
274     private boolean filter( Attributes attributes ) throws NamingException
275     {
276         NamingEnumeration list = attributes.getIDs();
277 
278         while ( list.hasMore() )
279         {
280             String attrId = ( String ) list.next();
281 
282             AttributeType type = null;
283 
284             if ( registry.hasAttributeType( attrId ) )
285             {
286                 type = registry.lookup( attrId );
287             }
288 
289             if ( type != null && type.getUsage() != UsageEnum.USERAPPLICATIONS )
290             {
291                 attributes.remove( attrId );
292             }
293         }
294         return true;
295     }
296 
297 
298     private void filter( Name dn, Attributes entry, String[] ids )
299             throws NamingException
300     {
301         // still need to protect against returning op attrs when ids is null
302         if ( ids == null )
303         {
304             OperationalAttributeService.this.filter( entry );
305             return;
306         }
307 
308         if ( dn.size() == 0 )
309         {
310             HashSet idsSet = new HashSet( ids.length );
311 
312             for ( int ii = 0; ii < ids.length; ii++ )
313             {
314                 idsSet.add( ids[ii].toLowerCase() );
315             }
316 
317             NamingEnumeration list = entry.getIDs();
318 
319             while ( list.hasMore() )
320             {
321                 String attrId = ( ( String ) list.nextElement() ).toLowerCase();
322 
323                 if ( !idsSet.contains( attrId ) )
324                 {
325                     entry.remove( attrId );
326                 }
327             }
328         }
329         
330         // do nothing past here since this explicity specifies which
331         // attributes to include - backends will automatically populate
332         // with right set of attributes using ids array
333     }
334 }