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.HashSet;
032    import java.util.List;
033    import java.util.concurrent.locks.Lock;
034    
035    import org.opends.server.api.Backend;
036    import org.opends.server.api.ClientConnection;
037    import org.opends.server.api.plugin.PluginResult;
038    import org.opends.server.controls.LDAPAssertionRequestControl;
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.CompareOperation;
043    import org.opends.server.core.CompareOperationWrapper;
044    import org.opends.server.core.DirectoryServer;
045    import org.opends.server.core.PluginConfigManager;
046    import org.opends.server.loggers.debug.DebugTracer;
047    import org.opends.server.types.*;
048    import org.opends.server.types.operation.PostOperationCompareOperation;
049    import org.opends.server.types.operation.PostResponseCompareOperation;
050    import org.opends.server.types.operation.PreOperationCompareOperation;
051    
052    import static org.opends.messages.CoreMessages.*;
053    import static org.opends.server.loggers.debug.DebugLogger.*;
054    import static org.opends.server.util.ServerConstants.*;
055    import static org.opends.server.util.StaticUtils.*;
056    
057    
058    
059    /**
060     * This class defines an operation that may be used to determine whether a
061     * specified entry in the Directory Server contains a given attribute-value
062     * pair.
063     */
064    public class LocalBackendCompareOperation
065           extends CompareOperationWrapper
066           implements PreOperationCompareOperation, PostOperationCompareOperation,
067                      PostResponseCompareOperation
068    {
069      /**
070       * The tracer object for the debug logger.
071       */
072      private static final DebugTracer TRACER = getTracer();
073    
074    
075    
076      // The backend in which the comparison is to be performed.
077      private Backend backend;
078    
079      // The client connection for this operation.
080      private ClientConnection clientConnection;
081    
082      // The DN of the entry to compare.
083      private DN entryDN;
084    
085      // The entry to be compared.
086      private Entry entry = null;
087    
088    
089    
090      /**
091       * Creates a new compare operation based on the provided compare operation.
092       *
093       * @param compare  the compare operation
094       */
095      public LocalBackendCompareOperation(CompareOperation compare)
096      {
097        super(compare);
098        LocalBackendWorkflowElement.attachLocalOperation (compare, this);
099      }
100    
101    
102    
103      /**
104       * Retrieves the entry to target with the compare operation.
105       *
106       * @return  The entry to target with the compare operation, or
107       *          <CODE>null</CODE> if the entry is not yet available.
108       */
109      public Entry getEntryToCompare()
110      {
111        return entry;
112      }
113    
114    
115    
116      /**
117       * Process this compare operation in a local backend.
118       *
119       * @param  backend  The backend in which the compare operation should be
120       *                  processed.
121       *
122       * @throws CanceledOperationException if this operation should be
123       * cancelled
124       */
125      void processLocalCompare(Backend backend) throws CanceledOperationException {
126        boolean executePostOpPlugins = false;
127    
128        this.backend = backend;
129    
130        clientConnection  = getClientConnection();
131    
132        // Get the plugin config manager that will be used for invoking plugins.
133        PluginConfigManager pluginConfigManager =
134             DirectoryServer.getPluginConfigManager();
135    
136    
137        // Get a reference to the client connection
138        ClientConnection clientConnection = getClientConnection();
139    
140    
141        // Check for a request to cancel this operation.
142        checkIfCanceled(false);
143    
144    
145        // Create a labeled block of code that we can break out of if a problem is
146        // detected.
147    compareProcessing:
148        {
149          // Process the entry DN to convert it from the raw form to the form
150          // required for the rest of the compare processing.
151          entryDN = getEntryDN();
152          if (entryDN == null)
153          {
154            break compareProcessing;
155          }
156    
157    
158          // If the target entry is in the server configuration, then make sure the
159          // requester has the CONFIG_READ privilege.
160          if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) &&
161              (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, this)))
162          {
163            appendErrorMessage(ERR_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES.get());
164            setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
165            break compareProcessing;
166          }
167    
168    
169          // Check for a request to cancel this operation.
170          checkIfCanceled(false);
171    
172    
173          // Grab a read lock on the entry.
174          Lock readLock = null;
175          for (int i=0; i < 3; i++)
176          {
177            readLock = LockManager.lockRead(entryDN);
178            if (readLock != null)
179            {
180              break;
181            }
182          }
183    
184          if (readLock == null)
185          {
186            setResultCode(DirectoryServer.getServerErrorResultCode());
187            appendErrorMessage(ERR_COMPARE_CANNOT_LOCK_ENTRY.get(
188                                    String.valueOf(entryDN)));
189    
190            break compareProcessing;
191          }
192    
193          try
194          {
195            // Get the entry.  If it does not exist, then fail.
196            try
197            {
198              entry = DirectoryServer.getEntry(entryDN);
199    
200              if (entry == null)
201              {
202                setResultCode(ResultCode.NO_SUCH_OBJECT);
203                appendErrorMessage(
204                        ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(entryDN)));
205    
206                // See if one of the entry's ancestors exists.
207                DN parentDN = entryDN.getParentDNInSuffix();
208                while (parentDN != null)
209                {
210                  try
211                  {
212                    if (DirectoryServer.entryExists(parentDN))
213                    {
214                      setMatchedDN(parentDN);
215                      break;
216                    }
217                  }
218                  catch (Exception e)
219                  {
220                    if (debugEnabled())
221                    {
222                      TRACER.debugCaught(DebugLogLevel.ERROR, e);
223                    }
224                    break;
225                  }
226    
227                  parentDN = parentDN.getParentDNInSuffix();
228                }
229    
230                break compareProcessing;
231              }
232            }
233            catch (DirectoryException de)
234            {
235              if (debugEnabled())
236              {
237                TRACER.debugCaught(DebugLogLevel.ERROR, de);
238              }
239    
240              setResultCode(de.getResultCode());
241              appendErrorMessage(de.getMessageObject());
242              break compareProcessing;
243            }
244    
245            // Check to see if there are any controls in the request.  If so, then
246            // see if there is any special processing required.
247            try
248            {
249              handleRequestControls();
250            }
251            catch (DirectoryException de)
252            {
253              if (debugEnabled())
254              {
255                TRACER.debugCaught(DebugLogLevel.ERROR, de);
256              }
257    
258              setResponseData(de);
259              break compareProcessing;
260            }
261    
262    
263            // Check to see if the client has permission to perform the
264            // compare.
265    
266            // FIXME: for now assume that this will check all permission
267            // pertinent to the operation. This includes proxy authorization
268            // and any other controls specified.
269    
270            // FIXME: earlier checks to see if the entry already exists may
271            // have already exposed sensitive information to the client.
272            if (! AccessControlConfigManager.getInstance().
273                       getAccessControlHandler().isAllowed(this))
274            {
275              setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
276              appendErrorMessage(ERR_COMPARE_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(
277                                      String.valueOf(entryDN)));
278              break compareProcessing;
279            }
280    
281            // Check for a request to cancel this operation.
282            checkIfCanceled(false);
283    
284    
285            // Invoke the pre-operation compare plugins.
286            executePostOpPlugins = true;
287            PluginResult.PreOperation preOpResult =
288                 pluginConfigManager.invokePreOperationComparePlugins(this);
289              if (!preOpResult.continueProcessing())
290              {
291                setResultCode(preOpResult.getResultCode());
292                appendErrorMessage(preOpResult.getErrorMessage());
293                setMatchedDN(preOpResult.getMatchedDN());
294                setReferralURLs(preOpResult.getReferralURLs());
295                break compareProcessing;
296              }
297    
298    
299            // Get the base attribute type and set of options.
300            String          baseName;
301            HashSet<String> options;
302            String rawAttributeType = getRawAttributeType();
303            int             semicolonPos = rawAttributeType.indexOf(';');
304            if (semicolonPos > 0)
305            {
306              baseName = toLowerCase(rawAttributeType.substring(0, semicolonPos));
307    
308              options = new HashSet<String>();
309              int nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
310              while (nextPos > 0)
311              {
312                options.add(rawAttributeType.substring(semicolonPos+1, nextPos));
313                semicolonPos = nextPos;
314                nextPos = rawAttributeType.indexOf(';', semicolonPos+1);
315              }
316    
317              options.add(rawAttributeType.substring(semicolonPos+1));
318            }
319            else
320            {
321              baseName = toLowerCase(rawAttributeType);
322              options  = null;
323            }
324    
325    
326            // Actually perform the compare operation.
327            AttributeType attrType = getAttributeType();
328            if (attrType == null)
329            {
330              attrType = DirectoryServer.getAttributeType(baseName, true);
331              setAttributeType(attrType);
332            }
333    
334            List<Attribute> attrList = entry.getAttribute(attrType, options);
335            if ((attrList == null) || attrList.isEmpty())
336            {
337              setResultCode(ResultCode.NO_SUCH_ATTRIBUTE);
338              if (options == null)
339              {
340                appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR.get(
341                                        String.valueOf(entryDN), baseName));
342              }
343              else
344              {
345                appendErrorMessage(WARN_COMPARE_OP_NO_SUCH_ATTR_WITH_OPTIONS.get(
346                                        String.valueOf(entryDN), baseName));
347              }
348            }
349            else
350            {
351              AttributeValue value = new AttributeValue(attrType,
352                                                        getAssertionValue());
353    
354              boolean matchFound = false;
355              for (Attribute a : attrList)
356              {
357                if (a.hasValue(value))
358                {
359                  matchFound = true;
360                  break;
361                }
362              }
363    
364              if (matchFound)
365              {
366                setResultCode(ResultCode.COMPARE_TRUE);
367              }
368              else
369              {
370                setResultCode(ResultCode.COMPARE_FALSE);
371              }
372            }
373          }
374          finally
375          {
376            LockManager.unlock(entryDN, readLock);
377          }
378        }
379    
380    
381        // Check for a request to cancel this operation.
382        checkIfCanceled(false);
383    
384    
385        // Invoke the post-operation compare plugins.
386        if (executePostOpPlugins)
387        {
388          PluginResult.PostOperation postOpResult =
389               pluginConfigManager.invokePostOperationComparePlugins(this);
390          if (!postOpResult.continueProcessing())
391          {
392            setResultCode(postOpResult.getResultCode());
393            appendErrorMessage(postOpResult.getErrorMessage());
394            setMatchedDN(postOpResult.getMatchedDN());
395            setReferralURLs(postOpResult.getReferralURLs());
396          }
397        }
398      }
399    
400    
401    
402      /**
403       * Performs any processing required for the controls included in the request.
404       *
405       * @throws  DirectoryException  If a problem occurs that should prevent the
406       *                              operation from succeeding.
407       */
408      private void handleRequestControls()
409              throws DirectoryException
410      {
411        List<Control> requestControls = getRequestControls();
412        if ((requestControls != null) && (! requestControls.isEmpty()))
413        {
414          for (int i=0; i < requestControls.size(); i++)
415          {
416            Control c   = requestControls.get(i);
417            String  oid = c.getOID();
418    
419            if (! AccessControlConfigManager.getInstance().
420                       getAccessControlHandler().isAllowed(entryDN, this, c))
421            {
422              throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
423                             ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
424            }
425    
426            if (oid.equals(OID_LDAP_ASSERTION))
427            {
428              LDAPAssertionRequestControl assertControl;
429              if (c instanceof LDAPAssertionRequestControl)
430              {
431                assertControl = (LDAPAssertionRequestControl) c;
432              }
433              else
434              {
435                try
436                {
437                  assertControl = LDAPAssertionRequestControl.decodeControl(c);
438                  requestControls.set(i, assertControl);
439                }
440                catch (LDAPException le)
441                {
442                  if (debugEnabled())
443                  {
444                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
445                  }
446    
447                  throw new DirectoryException(
448                                 ResultCode.valueOf(le.getResultCode()),
449                                 le.getMessageObject());
450                }
451              }
452    
453              try
454              {
455                // FIXME -- We need to determine whether the current user has
456                //          permission to make this determination.
457                SearchFilter filter = assertControl.getSearchFilter();
458                if (! filter.matchesEntry(entry))
459                {
460                  throw new DirectoryException(ResultCode.ASSERTION_FAILED,
461                                               ERR_COMPARE_ASSERTION_FAILED.get(
462                                                    String.valueOf(entryDN)));
463                }
464              }
465              catch (DirectoryException de)
466              {
467                if (de.getResultCode() == ResultCode.ASSERTION_FAILED)
468                {
469                  throw de;
470                }
471    
472                if (debugEnabled())
473                {
474                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
475                }
476    
477                throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
478                               ERR_COMPARE_CANNOT_PROCESS_ASSERTION_FILTER.get(
479                                    String.valueOf(entryDN),
480                                    de.getMessageObject()));
481              }
482            }
483            else if (oid.equals(OID_PROXIED_AUTH_V1))
484            {
485              // The requester must have the PROXIED_AUTH privilige in order to
486              // be able to use this control.
487              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
488              {
489                throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
490                               ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
491              }
492    
493    
494              ProxiedAuthV1Control proxyControl;
495              if (c instanceof ProxiedAuthV1Control)
496              {
497                proxyControl = (ProxiedAuthV1Control) c;
498              }
499              else
500              {
501                try
502                {
503                  proxyControl = ProxiedAuthV1Control.decodeControl(c);
504                }
505                catch (LDAPException le)
506                {
507                  if (debugEnabled())
508                  {
509                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
510                  }
511    
512                  throw new DirectoryException(
513                                 ResultCode.valueOf(le.getResultCode()),
514                                 le.getMessageObject());
515                }
516              }
517    
518    
519              Entry authorizationEntry = proxyControl.getAuthorizationEntry();
520              setAuthorizationEntry(authorizationEntry);
521              if (authorizationEntry == null)
522              {
523                setProxiedAuthorizationDN(DN.nullDN());
524              }
525              else
526              {
527                setProxiedAuthorizationDN(authorizationEntry.getDN());
528              }
529            }
530            else if (oid.equals(OID_PROXIED_AUTH_V2))
531            {
532              // The requester must have the PROXIED_AUTH privilige in order to
533              // be able to use this control.
534              if (! clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this))
535              {
536                throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED,
537                               ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
538              }
539    
540    
541              ProxiedAuthV2Control proxyControl;
542              if (c instanceof ProxiedAuthV2Control)
543              {
544                proxyControl = (ProxiedAuthV2Control) c;
545              }
546              else
547              {
548                try
549                {
550                  proxyControl = ProxiedAuthV2Control.decodeControl(c);
551                }
552                catch (LDAPException le)
553                {
554                  if (debugEnabled())
555                  {
556                    TRACER.debugCaught(DebugLogLevel.ERROR, le);
557                  }
558    
559                  throw new DirectoryException(
560                                 ResultCode.valueOf(le.getResultCode()),
561                                 le.getMessageObject());
562                }
563              }
564    
565    
566              Entry authorizationEntry = proxyControl.getAuthorizationEntry();
567              setAuthorizationEntry(authorizationEntry);
568              if (authorizationEntry == null)
569              {
570                setProxiedAuthorizationDN(DN.nullDN());
571              }
572              else
573              {
574                setProxiedAuthorizationDN(authorizationEntry.getDN());
575              }
576            }
577    
578            // NYI -- Add support for additional controls.
579            else if (c.isCritical())
580            {
581              if ((backend == null) || (! backend.supportsControl(oid)))
582              {
583                throw new DirectoryException(
584                               ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
585                               ERR_COMPARE_UNSUPPORTED_CRITICAL_CONTROL.get(
586                                    String.valueOf(entryDN), oid));
587              }
588            }
589          }
590        }
591      }
592    }
593