001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.workflowelement.localbackend;
028    
029    
030    
031    import java.util.List;
032    
033    import org.opends.server.api.Backend;
034    import org.opends.server.api.ClientConnection;
035    import org.opends.server.api.plugin.PluginResult;
036    import org.opends.server.controls.LDAPAssertionRequestControl;
037    import org.opends.server.controls.MatchedValuesControl;
038    import org.opends.server.controls.PersistentSearchControl;
039    import org.opends.server.controls.ProxiedAuthV1Control;
040    import org.opends.server.controls.ProxiedAuthV2Control;
041    import org.opends.server.core.AccessControlConfigManager;
042    import org.opends.server.core.DirectoryServer;
043    import org.opends.server.core.PersistentSearch;
044    import org.opends.server.core.PluginConfigManager;
045    import org.opends.server.core.SearchOperationWrapper;
046    import org.opends.server.core.SearchOperation;
047    import org.opends.server.loggers.debug.DebugTracer;
048    import org.opends.server.types.CanceledOperationException;
049    import org.opends.server.types.Control;
050    import org.opends.server.types.DebugLogLevel;
051    import org.opends.server.types.DirectoryException;
052    import org.opends.server.types.DN;
053    import org.opends.server.types.Entry;
054    import org.opends.server.types.LDAPException;
055    import org.opends.server.types.Privilege;
056    import org.opends.server.types.ResultCode;
057    import org.opends.server.types.SearchFilter;
058    import org.opends.server.types.operation.PostOperationSearchOperation;
059    import org.opends.server.types.operation.PreOperationSearchOperation;
060    import org.opends.server.types.operation.SearchEntrySearchOperation;
061    import org.opends.server.types.operation.SearchReferenceSearchOperation;
062    
063    import static org.opends.messages.CoreMessages.*;
064    import static org.opends.server.loggers.debug.DebugLogger.*;
065    import static org.opends.server.util.ServerConstants.*;
066    import static org.opends.server.util.StaticUtils.*;
067    
068    
069    
070    /**
071     * This class defines an operation used to search for entries in a local backend
072     * of the Directory Server.
073     */
074    public class LocalBackendSearchOperation
075           extends SearchOperationWrapper
076           implements PreOperationSearchOperation, PostOperationSearchOperation,
077                      SearchEntrySearchOperation, SearchReferenceSearchOperation
078    {
079      /**
080       * The tracer object for the debug logger.
081       */
082      private static final DebugTracer TRACER = getTracer();
083    
084    
085    
086      // The backend in which the search is to be performed.
087      private Backend backend;
088    
089      // Indicates whether we should actually process the search.  This should
090      // only be false if it's a persistent search with changesOnly=true.
091      private boolean processSearch;
092    
093      // The client connection for the search operation.
094      private ClientConnection clientConnection;
095    
096      // The base DN for the search.
097      private DN baseDN;
098    
099      // The persistent search request, if applicable.
100      private PersistentSearch persistentSearch;
101    
102      // The filter for the search.
103      private SearchFilter filter;
104    
105    
106    
107      /**
108       * Creates a new operation that may be used to search for entries in a local
109       * backend of the Directory Server.
110       *
111       * @param  search  The operation to process.
112       */
113      public LocalBackendSearchOperation(SearchOperation search)
114      {
115        super(search);
116        LocalBackendWorkflowElement.attachLocalOperation(search, this);
117      }
118    
119    
120    
121      /**
122       * Process this search operation against a local backend.
123       *
124       * @param  backend  The backend in which the search operation should be
125       *                  performed.
126       *
127       * @throws CanceledOperationException if this operation should be
128       * cancelled
129       */
130      void processLocalSearch(Backend backend) throws CanceledOperationException {
131        boolean executePostOpPlugins = false;
132    
133        this.backend = backend;
134    
135        clientConnection = getClientConnection();
136    
137        // Get the plugin config manager that will be used for invoking plugins.
138        PluginConfigManager pluginConfigManager =
139          DirectoryServer.getPluginConfigManager();
140        processSearch = true;
141    
142        // Check for a request to cancel this operation.
143        checkIfCanceled(false);
144    
145        // Create a labeled block of code that we can break out of if a problem is
146        // detected.
147    searchProcessing:
148        {
149          // Process the search base and filter to convert them from their raw forms
150          // as provided by the client to the forms required for the rest of the
151          // search processing.
152          baseDN = getBaseDN();
153          filter = getFilter();
154    
155          if ((baseDN == null) || (filter == null)){
156            break searchProcessing;
157          }
158    
159          // Check to see if there are any controls in the request.  If so, then
160          // see if there is any special processing required.
161          try
162          {
163            handleRequestControls();
164          }
165          catch (DirectoryException de)
166          {
167            if (debugEnabled())
168            {
169              TRACER.debugCaught(DebugLogLevel.ERROR, de);
170            }
171    
172            setResponseData(de);
173            break searchProcessing;
174          }
175    
176    
177          // Check to see if the client has permission to perform the
178          // search.
179    
180          // FIXME: for now assume that this will check all permission
181          // pertinent to the operation. This includes proxy authorization
182          // and any other controls specified.
183          if (! AccessControlConfigManager.getInstance().getAccessControlHandler().
184                     isAllowed(this))
185          {
186            setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
187            appendErrorMessage(ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
188                                    String.valueOf(baseDN)));
189            break searchProcessing;
190          }
191    
192          // Check for a request to cancel this operation.
193          checkIfCanceled(false);
194    
195    
196          // Invoke the pre-operation search plugins.
197          executePostOpPlugins = true;
198          PluginResult.PreOperation preOpResult =
199              pluginConfigManager.invokePreOperationSearchPlugins(this);
200          if (!preOpResult.continueProcessing())
201          {
202            setResultCode(preOpResult.getResultCode());
203            appendErrorMessage(preOpResult.getErrorMessage());
204            setMatchedDN(preOpResult.getMatchedDN());
205            setReferralURLs(preOpResult.getReferralURLs());
206            break searchProcessing;
207          }
208    
209    
210          // Check for a request to cancel this operation.
211          checkIfCanceled(false);
212    
213    
214          // Get the backend that should hold the search base.  If there is none,
215          // then fail.
216          if (backend == null)
217          {
218            setResultCode(ResultCode.NO_SUCH_OBJECT);
219            appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(
220                                    String.valueOf(baseDN)));
221            break searchProcessing;
222          }
223    
224    
225          // We'll set the result code to "success".  If a problem occurs, then it
226          // will be overwritten.
227          setResultCode(ResultCode.SUCCESS);
228    
229    
230          // If there's a persistent search, then register it with the server.
231          if (persistentSearch != null)
232          {
233            DirectoryServer.registerPersistentSearch(persistentSearch);
234            setSendResponse(false);
235          }
236    
237    
238          // Process the search in the backend and all its subordinates.
239          try
240          {
241            if (processSearch)
242            {
243              backend.search(this);
244            }
245          }
246          catch (DirectoryException de)
247          {
248            if (debugEnabled())
249            {
250              TRACER.debugCaught(DebugLogLevel.VERBOSE, de);
251            }
252    
253            setResponseData(de);
254    
255            if (persistentSearch != null)
256            {
257              DirectoryServer.deregisterPersistentSearch(persistentSearch);
258              setSendResponse(true);
259            }
260    
261            break searchProcessing;
262          }
263          catch (Exception e)
264          {
265            if (debugEnabled())
266            {
267              TRACER.debugCaught(DebugLogLevel.ERROR, e);
268            }
269    
270            setResultCode(DirectoryServer.getServerErrorResultCode());
271            appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get(
272                                    getExceptionMessage(e)));
273    
274            if (persistentSearch != null)
275            {
276              DirectoryServer.deregisterPersistentSearch(persistentSearch);
277              setSendResponse(true);
278            }
279    
280            break searchProcessing;
281          }
282        }
283    
284    
285        // Check for a request to cancel this operation.
286        checkIfCanceled(false);
287    
288        // Invoke the post-operation search plugins.
289        if (executePostOpPlugins)
290        {
291          PluginResult.PostOperation postOpResult =
292               pluginConfigManager.invokePostOperationSearchPlugins(this);
293          if (!postOpResult.continueProcessing())
294          {
295            setResultCode(postOpResult.getResultCode());
296            appendErrorMessage(postOpResult.getErrorMessage());
297            setMatchedDN(postOpResult.getMatchedDN());
298            setReferralURLs(postOpResult.getReferralURLs());
299          }
300        }
301      }
302    
303    
304      /**
305       * Handles any controls contained in the request.
306       *
307       * @throws  DirectoryException  If there is a problem with any of the request
308       *                              controls.
309       */
310      private void handleRequestControls()
311              throws DirectoryException
312      {
313        List<Control> requestControls  = getRequestControls();
314        if ((requestControls != null) && (! requestControls.isEmpty()))
315        {
316          for (int i=0; i < requestControls.size(); i++)
317          {
318            Control c   = requestControls.get(i);
319            String  oid = c.getOID();
320            if (! AccessControlConfigManager.getInstance().
321                       getAccessControlHandler().isAllowed(baseDN, this, c))
322            {
323              throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
324                             ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
325            }
326    
327            if (oid.equals(OID_LDAP_ASSERTION))
328            {
329              LDAPAssertionRequestControl assertControl;
330              if (c instanceof LDAPAssertionRequestControl)
331              {
332                assertControl = (LDAPAssertionRequestControl) c;
333              }
334              else
335              {
336                try
337                {
338                  assertControl = LDAPAssertionRequestControl.decodeControl(c);
339                  requestControls.set(i, assertControl);
340                }
341                catch (LDAPException le)
342                {
343                  if (debugEnabled())
344                  {
345                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
346                  }
347    
348                  throw new DirectoryException(
349                                 ResultCode.valueOf(le.getResultCode()),
350                                 le.getMessageObject(), le);
351                }
352              }
353    
354              try
355              {
356                // FIXME -- We need to determine whether the current user has
357                //          permission to make this determination.
358                SearchFilter assertionFilter = assertControl.getSearchFilter();
359                Entry entry;
360                try
361                {
362                  entry = DirectoryServer.getEntry(baseDN);
363                }
364                catch (DirectoryException de)
365                {
366                  if (debugEnabled())
367                  {
368                    TRACER.debugCaught(DebugLogLevel.ERROR, de);
369                  }
370    
371                  throw new DirectoryException(de.getResultCode(),
372                                 ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(
373                                      de.getMessageObject()));
374                }
375    
376                if (entry == null)
377                {
378                  throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
379                                 ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
380                }
381    
382                if (! assertionFilter.matchesEntry(entry))
383                {
384                  throw new DirectoryException(ResultCode.ASSERTION_FAILED,
385                                               ERR_SEARCH_ASSERTION_FAILED.get());
386                }
387              }
388              catch (DirectoryException de)
389              {
390                if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
391                {
392                  throw de;
393                }
394    
395                if (debugEnabled())
396                {
397                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
398                }
399    
400                throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
401                               ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(
402                                    de.getMessageObject()), de);
403              }
404            }
405            else if (oid.equals(OID_PROXIED_AUTH_V1))
406            {
407              // The requester must have the PROXIED_AUTH privilige in order to be
408              // able to use this control.
409              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
410              {
411                throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
412                               ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
413              }
414    
415    
416              ProxiedAuthV1Control proxyControl;
417              if (c instanceof ProxiedAuthV1Control)
418              {
419                proxyControl = (ProxiedAuthV1Control) c;
420              }
421              else
422              {
423                try
424                {
425                  proxyControl = ProxiedAuthV1Control.decodeControl(c);
426                }
427                catch (LDAPException le)
428                {
429                  if (debugEnabled())
430                  {
431                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
432                  }
433    
434                  throw new DirectoryException(
435                                 ResultCode.valueOf(le.getResultCode()),
436                                 le.getMessageObject(), le);
437                }
438              }
439    
440    
441              Entry authorizationEntry = proxyControl.getAuthorizationEntry();
442              setAuthorizationEntry(authorizationEntry);
443              if (authorizationEntry == null)
444              {
445                setProxiedAuthorizationDN(DN.nullDN());
446              }
447              else
448              {
449                setProxiedAuthorizationDN(authorizationEntry.getDN());
450              }
451            }
452            else if (oid.equals(OID_PROXIED_AUTH_V2))
453            {
454              // The requester must have the PROXIED_AUTH privilige in order to be
455              // able to use this control.
456              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
457              {
458                throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
459                               ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
460              }
461    
462    
463              ProxiedAuthV2Control proxyControl;
464              if (c instanceof ProxiedAuthV2Control)
465              {
466                proxyControl = (ProxiedAuthV2Control) c;
467              }
468              else
469              {
470                try
471                {
472                  proxyControl = ProxiedAuthV2Control.decodeControl(c);
473                }
474                catch (LDAPException le)
475                {
476                  if (debugEnabled())
477                  {
478                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
479                  }
480    
481                  throw new DirectoryException(
482                                 ResultCode.valueOf(le.getResultCode()),
483                                 le.getMessageObject(), le);
484                }
485              }
486    
487    
488              Entry authorizationEntry = proxyControl.getAuthorizationEntry();
489              setAuthorizationEntry(authorizationEntry);
490              if (authorizationEntry == null)
491              {
492                setProxiedAuthorizationDN(DN.nullDN());
493              }
494              else
495              {
496                setProxiedAuthorizationDN(authorizationEntry.getDN());
497              }
498            }
499            else if (oid.equals(OID_PERSISTENT_SEARCH))
500            {
501              PersistentSearchControl psearchControl;
502              if (c instanceof PersistentSearchControl)
503              {
504                psearchControl = (PersistentSearchControl) c;
505              }
506              else
507              {
508                try
509                {
510                  psearchControl = PersistentSearchControl.decodeControl(c);
511                }
512                catch (LDAPException le)
513                {
514                  if (debugEnabled())
515                  {
516                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
517                  }
518    
519                  throw new DirectoryException(
520                                 ResultCode.valueOf(le.getResultCode()),
521                                 le.getMessageObject(), le);
522                }
523              }
524    
525              persistentSearch = new PersistentSearch(this,
526                                          psearchControl.getChangeTypes(),
527                                          psearchControl.getReturnECs());
528              setPersistentSearch(persistentSearch);
529    
530              // If we're only interested in changes, then we don't actually want
531              // to process the search now.
532              if (psearchControl.getChangesOnly())
533              {
534                processSearch = false;
535              }
536            }
537            else if (oid.equals(OID_LDAP_SUBENTRIES))
538            {
539              setReturnLDAPSubentries(true);
540            }
541            else if (oid.equals(OID_MATCHED_VALUES))
542            {
543              if (c instanceof MatchedValuesControl)
544              {
545                setMatchedValuesControl((MatchedValuesControl) c);
546              }
547              else
548              {
549                try
550                {
551                  MatchedValuesControl matchedValuesControl =
552                    MatchedValuesControl.decodeControl(c);
553                  setMatchedValuesControl(matchedValuesControl);
554                }
555                catch (LDAPException le)
556                {
557                  if (debugEnabled())
558                  {
559                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
560                  }
561    
562                  throw new DirectoryException(
563                                 ResultCode.valueOf(le.getResultCode()),
564                                 le.getMessageObject(), le);
565                }
566              }
567            }
568            else if (oid.equals(OID_ACCOUNT_USABLE_CONTROL))
569            {
570              setIncludeUsableControl(true);
571            }
572            else if (oid.equals(OID_REAL_ATTRS_ONLY))
573            {
574              setRealAttributesOnly(true);
575            }
576            else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
577            {
578              setVirtualAttributesOnly(true);
579            }
580            else if (oid.equals(OID_GET_EFFECTIVE_RIGHTS) &&
581              DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS))
582            {
583              // Do nothing here and let AciHandler deal with it.
584            }
585    
586            // NYI -- Add support for additional controls.
587    
588            else if (c.isCritical())
589            {
590              if ((backend == null) || (! backend.supportsControl(oid)))
591              {
592                throw new DirectoryException(
593                               ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
594                               ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
595              }
596            }
597          }
598        }
599      }
600    }
601