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