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 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.controls;
018
019import java.io.IOException;
020import org.forgerock.i18n.LocalizableMessage;
021import org.opends.server.api.AuthenticationPolicyState;
022import org.opends.server.core.DirectoryServer;
023import org.opends.server.core.PasswordPolicyState;
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.opends.server.types.*;
029import org.forgerock.opendj.ldap.DN;
030import org.forgerock.opendj.ldap.ResultCode;
031import org.forgerock.opendj.ldap.ByteString;
032import static org.opends.messages.ProtocolMessages.*;
033import static org.opends.server.util.ServerConstants.*;
034import static org.opends.server.util.StaticUtils.*;
035
036/**
037 * This class implements version 1 of the proxied authorization control as
038 * defined in early versions of draft-weltman-ldapv3-proxy (this implementation
039 * is based on the "-04" revision).  It makes it possible for one user to
040 * request that an operation be performed under the authorization of another.
041 * The target user is specified as a DN in the control value, which
042 * distinguishes it from later versions of the control (which used a different
043 * OID) in which the target user was specified using an authorization ID.
044 */
045public class ProxiedAuthV1Control
046       extends Control
047{
048  /**
049   * ControlDecoder implementation to decode this control from a ByteString.
050   */
051  private static final class Decoder
052      implements ControlDecoder<ProxiedAuthV1Control>
053  {
054    /** {@inheritDoc} */
055    @Override
056    public ProxiedAuthV1Control decode(boolean isCritical, ByteString value)
057        throws DirectoryException
058    {
059      if (!isCritical)
060      {
061        LocalizableMessage message = ERR_PROXYAUTH1_CONTROL_NOT_CRITICAL.get();
062        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
063      }
064
065      if (value == null)
066      {
067        LocalizableMessage message = ERR_PROXYAUTH1_NO_CONTROL_VALUE.get();
068        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
069      }
070
071      ASN1Reader reader = ASN1.getReader(value);
072      DN authorizationDN;
073      try
074      {
075        reader.readStartSequence();
076        authorizationDN = DN.valueOf(reader.readOctetString());
077        reader.readEndSequence();
078      }
079      catch (Exception e)
080      {
081        logger.traceException(e);
082
083        LocalizableMessage message =
084            ERR_PROXYAUTH1_CANNOT_DECODE_VALUE.get(getExceptionMessage(e));
085        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message, e);
086      }
087
088      return new ProxiedAuthV1Control(isCritical, authorizationDN);
089    }
090
091    @Override
092    public String getOID()
093    {
094      return OID_PROXIED_AUTH_V1;
095    }
096
097  }
098
099  /**
100   * The Control Decoder that can be used to decode this control.
101   */
102  public static final ControlDecoder<ProxiedAuthV1Control> DECODER =
103    new Decoder();
104  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
105
106
107
108
109  /** The raw, unprocessed authorization DN from the control value. */
110  private ByteString rawAuthorizationDN;
111
112  /** The processed authorization DN from the control value. */
113  private DN authorizationDN;
114
115
116
117  /**
118   * Creates a new instance of the proxied authorization v1 control with the
119   * provided information.
120   *
121   * @param  rawAuthorizationDN  The raw, unprocessed authorization DN from the
122   *                             control value.  It must not be {@code null}.
123   */
124  public ProxiedAuthV1Control(ByteString rawAuthorizationDN)
125  {
126    this(true, rawAuthorizationDN);
127  }
128
129
130
131  /**
132   * Creates a new instance of the proxied authorization v1 control with the
133   * provided information.
134   *
135   * @param  authorizationDN  The authorization DN from the control value.  It
136   *                          must not be {@code null}.
137   */
138  public ProxiedAuthV1Control(DN authorizationDN)
139  {
140    this(true, authorizationDN);
141  }
142
143
144
145  /**
146   * Creates a new instance of the proxied authorization v1 control with the
147   * provided information.
148   *
149   * @param  isCritical          Indicates whether support for this control
150   *                             should be considered a critical part of the
151   *                             server processing.
152   * @param  rawAuthorizationDN  The raw, unprocessed authorization DN from the
153   *                             control value.
154   */
155  public ProxiedAuthV1Control(boolean isCritical, ByteString rawAuthorizationDN)
156  {
157    super(OID_PROXIED_AUTH_V1, isCritical);
158
159
160    this.rawAuthorizationDN = rawAuthorizationDN;
161
162    authorizationDN = null;
163  }
164
165
166
167  /**
168   * Creates a new instance of the proxied authorization v1 control with the
169   * provided information.
170   *
171   * @param  isCritical          Indicates whether support for this control
172   *                             should be considered a critical part of the
173   *                             server processing.
174   * @param  authorizationDN     The authorization DN from the control value.
175   *                             It must not be {@code null}.
176   */
177  public ProxiedAuthV1Control(boolean isCritical, DN authorizationDN)
178  {
179    super(OID_PROXIED_AUTH_V1, isCritical);
180
181
182    this.authorizationDN = authorizationDN;
183
184    rawAuthorizationDN = ByteString.valueOfUtf8(authorizationDN.toString());
185  }
186
187
188
189  /**
190   * Writes this control's value to an ASN.1 writer. The value (if any) must be
191   * written as an ASN1OctetString.
192   *
193   * @param writer The ASN.1 writer to use.
194   * @throws IOException If a problem occurs while writing to the stream.
195   */
196  @Override
197  protected void writeValue(ASN1Writer writer) throws IOException {
198    writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE);
199
200    writer.writeStartSequence();
201    writer.writeOctetString(rawAuthorizationDN);
202    writer.writeEndSequence();
203
204    writer.writeEndSequence();
205  }
206
207
208
209  /**
210   * Retrieves the raw, unprocessed authorization DN from the control value.
211   *
212   * @return  The raw, unprocessed authorization DN from the control value.
213   */
214  public ByteString getRawAuthorizationDN()
215  {
216    return rawAuthorizationDN;
217  }
218
219
220
221  /**
222   * Retrieves the authorization DN from the control value.
223   *
224   * @return  The authorization DN from the control value.
225   *
226   * @throws  DirectoryException  If a problem occurs while attempting to decode
227   *                              the raw authorization DN as a DN.
228   */
229  public DN getAuthorizationDN()
230         throws DirectoryException
231  {
232    if (authorizationDN == null)
233    {
234      authorizationDN = DN.valueOf(rawAuthorizationDN);
235    }
236
237    return authorizationDN;
238  }
239
240
241
242  /**
243   * Retrieves the authorization entry for this proxied authorization V1
244   * control.  It will also perform any necessary password policy checks to
245   * ensure that the associated user account is suitable for use in performing
246   * this processing.
247   *
248   * @return  The entry for user specified as the authorization identity in this
249   *          proxied authorization V1 control, or {@code null} if the
250   *          authorization DN is the null DN.
251   *
252   * @throws  DirectoryException  If the target user does not exist or is not
253   *                              available for use, or if a problem occurs
254   *                              while making the determination.
255   */
256  public Entry getAuthorizationEntry()
257         throws DirectoryException
258  {
259    DN authzDN = getAuthorizationDN();
260    if (authzDN.isRootDN())
261    {
262      return null;
263    }
264
265
266    // See if the authorization DN is one of the alternate bind DNs for one of
267    // the root users and if so then map it accordingly.
268    DN actualDN = DirectoryServer.getActualRootBindDN(authzDN);
269    if (actualDN != null)
270    {
271      authzDN = actualDN;
272    }
273
274
275    Entry userEntry = DirectoryServer.getEntry(authzDN);
276    if (userEntry == null)
277    {
278      // The requested user does not exist.
279      LocalizableMessage message = ERR_PROXYAUTH1_NO_SUCH_USER.get(authzDN);
280      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
281    }
282
283
284    // FIXME -- We should provide some mechanism for enabling debug
285    // processing.
286    AuthenticationPolicyState state = AuthenticationPolicyState.forUser(
287        userEntry, false);
288
289    if (state.isDisabled())
290    {
291      LocalizableMessage message = ERR_PROXYAUTH1_UNUSABLE_ACCOUNT.get(userEntry.getName());
292      throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
293    }
294
295    if (state.isPasswordPolicy())
296    {
297      PasswordPolicyState pwpState = (PasswordPolicyState) state;
298      if (pwpState.isAccountExpired() || pwpState.isLocked() || pwpState.isPasswordExpired())
299      {
300        LocalizableMessage message = ERR_PROXYAUTH1_UNUSABLE_ACCOUNT.get(authzDN);
301        throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
302      }
303    }
304
305    // If we've made it here, then the user is acceptable.
306    return userEntry;
307  }
308
309
310
311  /**
312   * Appends a string representation of this proxied auth v1 control to the
313   * provided buffer.
314   *
315   * @param  buffer  The buffer to which the information should be appended.
316   */
317  @Override
318  public void toString(StringBuilder buffer)
319  {
320    buffer.append("ProxiedAuthorizationV1Control(authorizationDN=\"");
321    buffer.append(rawAuthorizationDN);
322    buffer.append("\")");
323  }
324}
325