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 2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.controls;
028
029import java.io.IOException;
030import java.util.LinkedList;
031import java.util.List;
032
033import org.forgerock.i18n.LocalizableMessage;
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035import org.forgerock.opendj.io.ASN1;
036import org.forgerock.opendj.io.ASN1Reader;
037import org.forgerock.opendj.io.ASN1Writer;
038import org.forgerock.opendj.ldap.ByteString;
039import org.forgerock.opendj.ldap.ResultCode;
040import org.opends.server.core.DirectoryServer;
041import org.opends.server.types.AttributeType;
042import org.opends.server.types.Control;
043import org.opends.server.types.DN;
044import org.opends.server.types.DirectoryException;
045
046import static org.opends.messages.ProtocolMessages.*;
047import static org.opends.server.util.ServerConstants.*;
048
049/**
050 * This class partially implements the geteffectiverights control as defined
051 * in draft-ietf-ldapext-acl-model-08.txt. The main differences are:
052 *
053 *  - The response control is not supported. Instead the dseecompat
054 *    geteffectiverights control implementation creates attributes containing
055 *    right information strings and adds those attributes to the
056 *    entry being returned. The attribute type names are dynamically created;
057 *    see the dseecompat's AciGetEffectiveRights class for details.
058 *
059 *  - The dseecompat implementation allows additional attribute types
060 *    in the request control for which rights information can be returned.
061 *    These are known as the specified attribute types.
062 *
063 * The dseecompat request control value is the following:
064 *
065 * <BR>
066 * <PRE>
067 *  GetRightsControl ::= SEQUENCE {
068 *    authzId    authzId
069 *    attributes  SEQUENCE OF AttributeType
070 *  }
071 *
072 *   -- Only the "dn:DN form is supported.
073 *
074 * </PRE>
075 *
076 **/
077public class GetEffectiveRightsRequestControl extends Control
078{
079  /**
080   * ControlDecoder implementation to decode this control from a ByteString.
081   */
082  private static final class Decoder
083      implements ControlDecoder<GetEffectiveRightsRequestControl>
084  {
085    /** {@inheritDoc} */
086    public GetEffectiveRightsRequestControl decode(boolean isCritical,
087        ByteString value) throws DirectoryException
088    {
089      // If the value is null create a GetEffectiveRightsRequestControl
090      // class with null authzDN and attribute list, else try to
091      // decode the value.
092      if (value == null)
093      {
094        return new GetEffectiveRightsRequestControl(isCritical, (DN)null,
095            (List<AttributeType>)null);
096      }
097      else
098      {
099        ASN1Reader reader = ASN1.getReader(value);
100        DN authzDN;
101        List<AttributeType> attrs=null;
102        String authzIDString="";
103        try {
104          reader.readStartSequence();
105          authzIDString = reader.readOctetStringAsString();
106          String lowerAuthzIDString = authzIDString.toLowerCase();
107          //Make sure authzId starts with "dn:" and is a valid DN.
108          if (lowerAuthzIDString.startsWith("dn:"))
109          {
110            authzDN = DN.valueOf(authzIDString.substring(3));
111          }
112          else {
113            LocalizableMessage message = INFO_GETEFFECTIVERIGHTS_INVALID_AUTHZID.get(
114                lowerAuthzIDString);
115            throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
116          }
117          //There is an sequence containing an attribute list, try to decode it.
118          if(reader.hasNextElement()) {
119            attrs = new LinkedList<>();
120            reader.readStartSequence();
121            while(reader.hasNextElement()) {
122              String attrStr = reader.readOctetStringAsString();
123              attrs.add(DirectoryServer.getAttributeTypeOrDefault(attrStr));
124            }
125            reader.readEndSequence();
126          }
127          reader.readEndSequence();
128        } catch (IOException e) {
129          logger.traceException(e);
130
131          LocalizableMessage message =
132              INFO_GETEFFECTIVERIGHTS_DECODE_ERROR.get(e.getMessage());
133          throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
134        }
135
136        return new GetEffectiveRightsRequestControl(isCritical,
137            authzDN, attrs);
138      }
139    }
140
141    public String getOID()
142    {
143      return OID_GET_EFFECTIVE_RIGHTS;
144    }
145  }
146
147  /**
148   * The Control Decoder that can be used to decode this control.
149   */
150  public static final ControlDecoder<GetEffectiveRightsRequestControl> DECODER =
151    new Decoder();
152  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
153
154  /** The DN representing the authzId. May be null. */
155  private DN authzDN;
156
157  /** The raw DN representing the authzId. May be null. */
158  private String rawAuthzDN;
159
160  /** The list of additional attribute types to return rights for. May be null. */
161  private List<AttributeType> attrs;
162
163  /** The raw DN representing the authzId. May be null. */
164  private List<String> rawAttrs;
165
166  /**
167   * Create a new geteffectiverights control with the specified authzDN and
168   * an attribute list.
169   *
170   * @param authzDN  The authzDN.
171   *
172   * @param attrs  The list of additional attributes to be returned.
173   */
174  public GetEffectiveRightsRequestControl(DN authzDN,
175                            List<AttributeType> attrs) {
176    this(true, authzDN, attrs);
177  }
178
179  /**
180   * Create a new geteffectiverights control with the specified authzDN and
181   * an attribute list.
182   *
183   * @param  isCritical  Indicates whether this control should be
184   *                     considered critical in processing the
185   *                     request.
186   * @param authzDN  The authzDN.
187   * @param attrs  The list of additional attributes to be returned.
188   */
189  public GetEffectiveRightsRequestControl(boolean isCritical, DN authzDN,
190                                          List<AttributeType> attrs) {
191    super(OID_GET_EFFECTIVE_RIGHTS, isCritical);
192    this.authzDN=authzDN;
193    this.attrs=attrs;
194  }
195
196  /**
197   * Create a new geteffectiverights control with the specified raw
198   * authzDN and an attribute list.
199   *
200   * @param  isCritical  Indicates whether this control should be
201   *                     considered critical in processing the
202   *                     request.
203   * @param authzDN  The authzDN.
204   * @param attrs  The list of additional attributes to be returned.
205   */
206  public GetEffectiveRightsRequestControl(boolean isCritical,
207                                          String authzDN,
208                                          List<String> attrs)
209  {
210    super(OID_GET_EFFECTIVE_RIGHTS, isCritical);
211    this.rawAuthzDN=authzDN;
212    this.rawAttrs=attrs;
213  }
214
215  /**
216   * Writes this control's value to an ASN.1 writer. The value (if any) must be
217   * written as an ASN1OctetString.
218   *
219   * @param writer The ASN.1 output stream to write to.
220   * @throws IOException If a problem occurs while writing to the stream.
221   */
222  @Override
223  public void writeValue(ASN1Writer writer) throws IOException {
224    writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE);
225
226    writer.writeStartSequence();
227    if(authzDN != null)
228    {
229      writer.writeOctetString("dn:" + authzDN);
230    }
231    else if(rawAuthzDN != null)
232    {
233      writer.writeOctetString("dn:" + rawAuthzDN);
234    }
235
236    if(attrs != null)
237    {
238      writer.writeStartSequence();
239      for(AttributeType attr : attrs)
240      {
241        writer.writeOctetString(attr.getNameOrOID());
242      }
243      writer.writeEndSequence();
244    }
245    else if(rawAttrs != null)
246    {
247      writer.writeStartSequence();
248      for(String attr : rawAttrs)
249      {
250        writer.writeOctetString(attr);
251      }
252      writer.writeEndSequence();
253    }
254    writer.writeEndSequence();
255
256    writer.writeEndSequence();
257  }
258
259  /**
260   * Return the authzDN parsed from the control.
261   *
262   * @return The DN representing the authzId.
263   */
264  public DN getAuthzDN () {
265    return authzDN;
266    // TODO: what if rawAuthzDN is not null?
267  }
268
269  /**
270   * Return the requested additional attributes parsed from the control. Known
271   * as the specified attributes.
272   *
273   * @return  The list containing any additional attributes to return rights
274   *          about.
275   */
276  public List<AttributeType> getAttributes() {
277    return attrs;
278  }
279}