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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.controls;
028import org.forgerock.i18n.LocalizableMessage;
029
030
031
032import java.util.ArrayList;
033import java.io.IOException;
034
035import org.forgerock.opendj.io.*;
036import org.opends.server.types.*;
037import org.forgerock.opendj.ldap.ResultCode;
038import org.forgerock.opendj.ldap.ByteString;
039import org.forgerock.i18n.slf4j.LocalizedLogger;
040import static org.opends.messages.ProtocolMessages.*;
041import static org.opends.server.util.ServerConstants.*;
042import static org.opends.server.util.StaticUtils.*;
043
044
045
046/**
047 * This class implements the matched values control as defined in RFC 3876.  It
048 * may be included in a search request to indicate that only attribute values
049 * matching one or more filters contained in the matched values control should
050 * be returned to the client.
051 */
052public class MatchedValuesControl
053       extends Control
054{
055  /**
056   * ControlDecoder implementation to decode this control from a ByteString.
057   */
058  private static final class Decoder
059      implements ControlDecoder<MatchedValuesControl>
060  {
061    /** {@inheritDoc} */
062    public MatchedValuesControl decode(boolean isCritical, ByteString value)
063        throws DirectoryException
064    {
065      ArrayList<MatchedValuesFilter> filters;
066      if (value == null)
067      {
068        LocalizableMessage message = ERR_MATCHEDVALUES_NO_CONTROL_VALUE.get();
069        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
070      }
071
072      ASN1Reader reader = ASN1.getReader(value);
073      try
074      {
075        reader.readStartSequence();
076        if (!reader.hasNextElement())
077        {
078          LocalizableMessage message = ERR_MATCHEDVALUES_NO_FILTERS.get();
079          throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
080        }
081
082        filters = new ArrayList<>();
083        while(reader.hasNextElement())
084        {
085          filters.add(MatchedValuesFilter.decode(reader));
086        }
087        reader.readEndSequence();
088      }
089      catch (DirectoryException e)
090      {
091        throw e;
092      }
093      catch (Exception e)
094      {
095        logger.traceException(e);
096
097        LocalizableMessage message = ERR_MATCHEDVALUES_CANNOT_DECODE_VALUE_AS_SEQUENCE.get(
098            getExceptionMessage(e));
099        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
100      }
101
102      return new MatchedValuesControl(isCritical,filters);
103    }
104
105
106    public String getOID()
107    {
108      return OID_MATCHED_VALUES;
109    }
110
111  }
112
113  /**
114   * The Control Decoder that can be used to decode this control.
115   */
116  public static final ControlDecoder<MatchedValuesControl> DECODER =
117    new Decoder();
118  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
119
120
121
122
123  /** The set of matched values filters for this control. */
124  private final ArrayList<MatchedValuesFilter> filters;
125
126
127
128  /**
129   * Creates a new matched values control using the default OID and the provided
130   * criticality and set of filters.
131   *
132   * @param  isCritical  Indicates whether this control should be considered
133   *                     critical to the operation processing.
134   * @param  filters     The set of filters to use to determine which values to
135   *                     return.
136   */
137  public MatchedValuesControl(boolean isCritical,
138                              ArrayList<MatchedValuesFilter> filters)
139  {
140    super(OID_MATCHED_VALUES, isCritical);
141
142
143    this.filters = filters;
144  }
145
146
147
148  /**
149   * Writes this control's value to an ASN.1 writer. The value (if any) must be
150   * written as an ASN1OctetString.
151   *
152   * @param writer The ASN.1 output stream to write to.
153   * @throws IOException If a problem occurs while writing to the stream.
154   */
155  @Override
156  public void writeValue(ASN1Writer writer) throws IOException {
157    writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE);
158
159    writer.writeStartSequence();
160    for (MatchedValuesFilter f : filters)
161    {
162      f.encode(writer);
163    }
164    writer.writeEndSequence();
165
166    writer.writeEndSequence();
167  }
168
169
170  /**
171   * Retrieves the set of filters associated with this matched values control.
172   *
173   * @return  The set of filters associated with this matched values control.
174   */
175  public ArrayList<MatchedValuesFilter> getFilters()
176  {
177    return filters;
178  }
179
180
181
182  /**
183   * Indicates whether any of the filters associated with this matched values
184   * control matches the provided attribute type/value.
185   *
186   * @param  type   The attribute type with which the value is associated.
187   * @param  value  The attribute value for which to make the determination.
188   *
189   * @return  <CODE>true</CODE> if at least one of the filters associated with
190   *          this matched values control does match the provided attribute
191   *          value, or <CODE>false</CODE> if none of the filters match.
192   */
193  public boolean valueMatches(AttributeType type, ByteString value)
194  {
195    for (MatchedValuesFilter f : filters)
196    {
197      try
198      {
199        if (f.valueMatches(type, value))
200        {
201          return true;
202        }
203      }
204      catch (Exception e)
205      {
206        logger.traceException(e);
207      }
208    }
209
210    return false;
211  }
212
213
214
215  /**
216   * Appends a string representation of this authorization identity response
217   * control to the provided buffer.
218   *
219   * @param  buffer  The buffer to which the information should be appended.
220   */
221  @Override
222  public void toString(StringBuilder buffer)
223  {
224    if (filters.size() == 1)
225    {
226      buffer.append("MatchedValuesControl(filter=\"");
227      filters.get(0).toString(buffer);
228      buffer.append("\")");
229    }
230    else
231    {
232      buffer.append("MatchedValuesControl(filters=\"(");
233
234      for (MatchedValuesFilter f : filters)
235      {
236        f.toString(buffer);
237      }
238
239      buffer.append(")\")");
240    }
241  }
242}
243