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