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.exception;
18  
19  
20  import java.util.Map;
21  
22  import javax.naming.Name;
23  import javax.naming.NamingEnumeration;
24  import javax.naming.NamingException;
25  import javax.naming.directory.*;
26  
27  import org.apache.ldap.common.exception.*;
28  import org.apache.ldap.common.filter.ExprNode;
29  import org.apache.ldap.common.message.ResultCodeEnum;
30  import org.apache.ldap.common.name.LdapName;
31  import org.apache.ldap.server.DirectoryServiceConfiguration;
32  import org.apache.ldap.server.invocation.Invocation;
33  import org.apache.ldap.server.invocation.InvocationStack;
34  import org.apache.ldap.server.configuration.InterceptorConfiguration;
35  import org.apache.ldap.server.interceptor.BaseInterceptor;
36  import org.apache.ldap.server.interceptor.NextInterceptor;
37  import org.apache.ldap.server.partition.DirectoryPartition;
38  import org.apache.ldap.server.partition.DirectoryPartitionNexus;
39  import org.apache.ldap.server.partition.DirectoryPartitionNexusProxy;
40  
41  
42  /***
43   * An {@link org.apache.ldap.server.interceptor.Interceptor} that detects any operations that breaks integrity
44   * of {@link DirectoryPartition} and terminates the current invocation chain by
45   * throwing a {@link NamingException}. Those operations include when an entry
46   * already exists at a DN and is added once again to the same DN.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   * @version $Rev: 326825 $
50   */
51  public class ExceptionService extends BaseInterceptor
52  {
53      private DirectoryPartitionNexus nexus;
54  
55      /***
56       * Creates an interceptor that is also the exception handling service.
57       */
58      public ExceptionService()
59      {
60      }
61  
62  
63      public void init( DirectoryServiceConfiguration factoryCfg, InterceptorConfiguration cfg )
64      {
65          nexus = factoryCfg.getPartitionNexus();
66      }
67  
68  
69      public void destroy()
70      {
71      }
72  
73  
74      /***
75       * In the pre-invocation state this interceptor method checks to see if the entry to be added already exists.  If it
76       * does an exception is raised.
77       */
78      public void add( NextInterceptor nextInterceptor, String upName, Name normName, Attributes entry ) throws NamingException
79      {
80          // check if the entry already exists
81          if ( nextInterceptor.hasEntry( normName ) )
82          {
83              NamingException ne = new LdapNameAlreadyBoundException( normName.toString() + " already exists!" );
84              ne.setResolvedName( new LdapName( upName ) );
85              throw ne;
86          }
87  
88          Name parentDn = normName.getPrefix( 1 );
89  
90          // check if we don't have the parent to add to
91          assertHasEntry( nextInterceptor, "Attempt to add under non-existant parent: ", parentDn );
92  
93          // check if we're trying to add to a parent that is an alias
94          Attributes attrs = nextInterceptor.lookup( normName.getPrefix( 1 ) );
95          Attribute objectClass = attrs.get( "objectClass" );
96          if ( objectClass.contains( "alias" ) )
97          {
98              String msg = "Attempt to add entry to alias '" + upName
99                      + "' not allowed.";
100             ResultCodeEnum rc = ResultCodeEnum.ALIASPROBLEM;
101             NamingException e = new LdapNamingException( msg, rc );
102             e.setResolvedName( parentDn );
103             throw e;
104         }
105 
106         nextInterceptor.add( upName, normName, entry );
107     }
108 
109 
110     /***
111      * Checks to make sure the entry being deleted exists, and has no children, otherwise throws the appropriate
112      * LdapException.
113      */
114     public void delete( NextInterceptor nextInterceptor, Name name ) throws NamingException
115     {
116         // check if entry to delete exists
117         String msg = "Attempt to delete non-existant entry: ";
118         assertHasEntry( nextInterceptor, msg, name );
119 
120         // check if entry to delete has children (only leaves can be deleted)
121         boolean hasChildren = false;
122         NamingEnumeration list = nextInterceptor.list( name );
123         if ( list.hasMore() )
124         {
125             hasChildren = true;
126         }
127 
128         list.close();
129         if ( hasChildren )
130         {
131             LdapContextNotEmptyException e = new LdapContextNotEmptyException();
132             e.setResolvedName( name );
133             throw e;
134         }
135 
136         nextInterceptor.delete( name );
137     }
138 
139 
140     /***
141      * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
142      */
143     public NamingEnumeration list( NextInterceptor nextInterceptor, Name baseName ) throws NamingException
144     {
145         // check if entry to search exists
146         String msg = "Attempt to search under non-existant entry: ";
147         assertHasEntry( nextInterceptor, msg, baseName );
148 
149         return nextInterceptor.list( baseName );
150     }
151 
152 
153     /***
154      * Checks to make sure the entry being looked up exists other wise throws the appropriate LdapException.
155      */
156     public Attributes lookup( NextInterceptor nextInterceptor, Name name ) throws NamingException
157     {
158         String msg = "Attempt to lookup non-existant entry: ";
159         assertHasEntry( nextInterceptor, msg, name );
160 
161         return nextInterceptor.lookup( name );
162     }
163 
164 
165     /***
166      * Checks to see the base being searched exists, otherwise throws the appropriate LdapException.
167      */
168     public Attributes lookup( NextInterceptor nextInterceptor, Name name, String[] attrIds ) throws NamingException
169     {
170         // check if entry to lookup exists
171         String msg = "Attempt to lookup non-existant entry: ";
172         assertHasEntry( nextInterceptor, msg, name );
173 
174         return nextInterceptor.lookup( name, attrIds );
175     }
176 
177 
178     /***
179      * Checks to see the entry being modified exists, otherwise throws the appropriate LdapException.
180      */
181     public void modify( NextInterceptor nextInterceptor, Name name, int modOp, Attributes attrs ) throws NamingException
182     {
183         // check if entry to modify exists
184         String msg = "Attempt to modify non-existant entry: ";
185         assertHasEntry( nextInterceptor, msg, name );
186 
187         Attributes entry = nexus.lookup( name );
188         NamingEnumeration attrIds = attrs.getIDs();
189         while ( attrIds.hasMore() )
190         {
191             String attrId = ( String ) attrIds.next();
192             Attribute modAttr = attrs.get( attrId );
193             Attribute entryAttr = entry.get( attrId );
194 
195             if ( modOp == DirContext.ADD_ATTRIBUTE )
196             {
197                 if ( entryAttr != null )
198                 {
199                     for ( int ii = 0; ii < modAttr.size(); ii++ )
200                     {
201                         if ( entryAttr.contains( modAttr.get( ii ) ) )
202                         {
203                             throw new LdapAttributeInUseException( "Trying to add existing value '"
204                                     + modAttr.get( ii ) + "' to attribute " + attrId );
205                         }
206                     }
207                 }
208             }
209         }
210         nextInterceptor.modify( name, modOp, attrs );
211     }
212 
213 
214     /***
215      * Checks to see the entry being modified exists, otherwise throws the appropriate LdapException.
216      */
217     public void modify( NextInterceptor nextInterceptor, Name name, ModificationItem[] items ) throws NamingException
218     {
219         // check if entry to modify exists
220         String msg = "Attempt to modify non-existant entry: ";
221         assertHasEntry( nextInterceptor, msg, name );
222 
223         Attributes entry = nexus.lookup( name );
224         for ( int ii = 0; ii < items.length; ii++ )
225         {
226             if ( items[ii].getModificationOp() == DirContext.ADD_ATTRIBUTE )
227             {
228                 Attribute modAttr = items[ii].getAttribute();
229                 Attribute entryAttr = entry.get( modAttr.getID() );
230 
231                 if ( entryAttr != null )
232                 {
233                     for ( int jj = 0; jj < modAttr.size(); jj++ )
234                     {
235                         if ( entryAttr.contains( modAttr.get( jj ) ) )
236                         {
237                             throw new LdapAttributeInUseException( "Trying to add existing value '"
238                                     + modAttr.get( ii ) + "' to attribute " + modAttr.getID() );
239                         }
240                     }
241                 }
242             }
243         }
244         nextInterceptor.modify( name, items );
245     }
246 
247 
248     /***
249      * Checks to see the entry being renamed exists, otherwise throws the appropriate LdapException.
250      */
251     public void modifyRn( NextInterceptor nextInterceptor, Name dn, String newRn, boolean deleteOldRn ) throws NamingException
252     {
253         // check if entry to rename exists
254         String msg = "Attempt to rename non-existant entry: ";
255         assertHasEntry( nextInterceptor, msg, dn );
256 
257         // check to see if target entry exists
258         Name target = dn.getPrefix( 1 ).add( newRn );
259         if ( nextInterceptor.hasEntry( target ) )
260         {
261             LdapNameAlreadyBoundException e = null;
262             e = new LdapNameAlreadyBoundException( "target entry " + target
263                     + " already exists!" );
264             e.setResolvedName( target );
265             throw e;
266         }
267 
268         nextInterceptor.modifyRn( dn, newRn, deleteOldRn );
269     }
270 
271 
272     /***
273      * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
274      * LdapException.
275      */
276     public void move( NextInterceptor nextInterceptor, Name oriChildName, Name newParentName ) throws NamingException
277     {
278         // check if child to move exists
279         String msg = "Attempt to move to non-existant parent: ";
280         assertHasEntry( nextInterceptor, msg, oriChildName );
281 
282         // check if parent to move to exists
283         msg = "Attempt to move to non-existant parent: ";
284         assertHasEntry( nextInterceptor, msg, newParentName );
285 
286         // check to see if target entry exists
287         String rdn = oriChildName.get( oriChildName.size() - 1 );
288         Name target = ( Name ) newParentName.clone();
289         target.add( rdn );
290         if ( nextInterceptor.hasEntry( target ) )
291         {
292             LdapNameAlreadyBoundException e = null;
293             e = new LdapNameAlreadyBoundException( "target entry " + target
294                     + " already exists!" );
295             e.setResolvedName( target );
296             throw e;
297         }
298 
299         nextInterceptor.move( oriChildName, newParentName );
300     }
301 
302 
303     /***
304      * Checks to see the entry being moved exists, and so does its parent, otherwise throws the appropriate
305      * LdapException.
306      */
307     public void move( NextInterceptor nextInterceptor,
308                       Name oriChildName, Name newParentName, String newRn,
309                       boolean deleteOldRn ) throws NamingException
310     {
311         // check if child to move exists
312         String msg = "Attempt to move to non-existant parent: ";
313         assertHasEntry( nextInterceptor, msg, oriChildName );
314 
315         // check if parent to move to exists
316         msg = "Attempt to move to non-existant parent: ";
317         assertHasEntry( nextInterceptor, msg, newParentName );
318 
319         // check to see if target entry exists
320         Name target = ( Name ) newParentName.clone();
321         target.add( newRn );
322         if ( nextInterceptor.hasEntry( target ) )
323         {
324             LdapNameAlreadyBoundException e = null;
325             e = new LdapNameAlreadyBoundException( "target entry " + target
326                     + " already exists!" );
327             e.setResolvedName( target );
328             throw e;
329         }
330 
331         nextInterceptor.move( oriChildName, newParentName, newRn, deleteOldRn );
332     }
333 
334 
335     /***
336      * Checks to see the entry being searched exists, otherwise throws the appropriate LdapException.
337      */
338     public NamingEnumeration search( NextInterceptor nextInterceptor,
339                                      Name base, Map env, ExprNode filter,
340                                      SearchControls searchCtls ) throws NamingException
341     {
342         String msg = "Attempt to search under non-existant entry: ";
343 
344         if ( base.size() == 0 )
345         {
346             return nextInterceptor.search( base, env, filter, searchCtls );
347         }
348 
349         Attribute attr = nextInterceptor.getRootDSE().get( "subschemaSubentry" );
350         if ( ( ( String ) attr.get() ).equalsIgnoreCase( base.toString() ) )
351         {
352             return nextInterceptor.search( base, env, filter, searchCtls );
353         }
354 
355         assertHasEntry( nextInterceptor, msg, base );
356 
357         return nextInterceptor.search( base, env, filter, searchCtls );
358     }
359 
360 
361     /***
362      * Asserts that an entry is present and as a side effect if it is not, creates a LdapNameNotFoundException, which is
363      * used to set the before exception on the invocation - eventually the exception is thrown.
364      *
365      * @param msg        the message to prefix to the distinguished name for explanation
366      * @param dn         the distinguished name of the entry that is asserted
367      * @throws NamingException if the entry does not exist
368      */
369     private void assertHasEntry( NextInterceptor nextInterceptor, String msg, Name dn ) throws NamingException
370     {
371         Invocation invocation = InvocationStack.getInstance().peek();
372         DirectoryPartitionNexusProxy proxy = invocation.getProxy();
373         if ( !nextInterceptor.hasEntry( dn ) )
374         {
375             LdapNameNotFoundException e = null;
376 
377             if ( msg != null )
378             {
379                 e = new LdapNameNotFoundException( msg + dn );
380             }
381             else
382             {
383                 e = new LdapNameNotFoundException( dn.toString() );
384             }
385 
386             e.setResolvedName( proxy.getMatchedName( dn, false ) );
387             throw e;
388         }
389     }
390 }