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