001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2007-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.controls;
037
038
039
040import java.util.List;
041
042import com.unboundid.asn1.ASN1OctetString;
043import com.unboundid.ldap.sdk.Control;
044import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
045import com.unboundid.ldap.sdk.LDAPException;
046import com.unboundid.ldap.sdk.ResultCode;
047import com.unboundid.util.NotMutable;
048import com.unboundid.util.NotNull;
049import com.unboundid.util.ThreadSafety;
050import com.unboundid.util.ThreadSafetyLevel;
051import com.unboundid.util.Validator;
052import com.unboundid.util.json.JSONField;
053import com.unboundid.util.json.JSONObject;
054
055import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
056
057
058
059/**
060 * This class provides an implementation of the proxied authorization V2
061 * request control, as defined in
062 * <A HREF="http://www.ietf.org/rfc/rfc4370.txt">RFC 4370</A>.  It may be used
063 * to request that the associated operation be performed as if it has been
064 * requested by some other user.
065 * <BR><BR>
066 * The target authorization identity for this control is specified as an
067 * "authzId" value as described in section 5.2.1.8 of
068 * <A HREF="http://www.ietf.org/rfc/rfc4513.txt">RFC 4513</A>.  That is, it
069 * should be either "dn:" followed by the distinguished name of the target user,
070 * or "u:" followed by the username.  If the "u:" form is used, then the
071 * mechanism used to resolve the provided username to an entry may vary from
072 * server to server.
073 * <BR><BR>
074 * This control may be used in conjunction with add, delete, compare, delete,
075 * extended, modify, modify DN, and search requests.  In that case, the
076 * associated operation will be processed under the authority of the specified
077 * authorization identity rather than the identity associated with the client
078 * connection (i.e., the user as whom that connection is bound).  Note that
079 * because of the inherent security risks associated with the use of the proxied
080 * authorization control, most directory servers which support its use enforce
081 * strict restrictions on the users that are allowed to request this control.
082 * If a user attempts to use the proxied authorization V2 request control and
083 * does not have sufficient permission to do so, then the server will return a
084 * failure response with the {@link ResultCode#AUTHORIZATION_DENIED} result
085 * code.
086 * <BR><BR>
087 * There is no corresponding response control for this request control.
088 * <BR><BR>
089 * <H2>Example</H2>
090 * The following example demonstrates the use of the proxied authorization V2
091 * control to delete an entry under the authority of the user with username
092 * "alternate.user":
093 * <PRE>
094 * // Create a delete request to delete an entry.  Include the proxied
095 * // authorization v2 request control in the delete request so that the
096 * // delete will be processed as the user with username "alternate.user"
097 * // instead of the user that's actually authenticated on the connection.
098 * DeleteRequest deleteRequest =
099 *      new DeleteRequest("uid=test.user,ou=People,dc=example,dc=com");
100 * deleteRequest.addControl(new ProxiedAuthorizationV2RequestControl(
101 *      "u:alternate.user"));
102 *
103 * LDAPResult deleteResult;
104 * try
105 * {
106 *   deleteResult = connection.delete(deleteRequest);
107 *   // If we got here, then the delete was successful.
108 * }
109 * catch (LDAPException le)
110 * {
111 *   // The delete failed for some reason.  In addition to all of the normal
112 *   // reasons a delete could fail (e.g., the entry doesn't exist, or has one
113 *   // or more subordinates), proxied-authorization specific failures may
114 *   // include that the authenticated user doesn't have permission to use the
115 *   // proxied authorization control to impersonate the alternate user, that
116 *   // the alternate user doesn't exist, or that the alternate user doesn't
117 *   // have permission to perform the requested operation.
118 *   deleteResult = le.toLDAPResult();
119 *   ResultCode resultCode = le.getResultCode();
120 *   String errorMessageFromServer = le.getDiagnosticMessage();
121 * }
122 * </PRE>
123 */
124@NotMutable()
125@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
126public final class ProxiedAuthorizationV2RequestControl
127       extends Control
128{
129  /**
130   * The OID (2.16.840.1.113730.3.4.18) for the proxied authorization v2 request
131   * control.
132   */
133  @NotNull public static final String PROXIED_AUTHORIZATION_V2_REQUEST_OID =
134       "2.16.840.1.113730.3.4.18";
135
136
137
138  /**
139   * The name of the field used to hold the authorization ID in the JSON
140   * representation of this control.
141   */
142  @NotNull private static final String JSON_FIELD_AUTHORIZATION_ID =
143       "authorization-id";
144
145
146
147  /**
148   * The serial version UID for this serializable class.
149   */
150  private static final long serialVersionUID = 1054244283964851067L;
151
152
153
154  // The authorization ID string that may be used to identify the user under
155  // whose authorization the associated operation should be performed.
156  @NotNull private final String authorizationID;
157
158
159
160  /**
161   * Creates a new proxied authorization V2 request control that will proxy as
162   * the specified user.
163   *
164   * @param  authorizationID  The authorization ID string that will be used to
165   *                          identify the user under whose authorization the
166   *                          associated operation should be performed.  It may
167   *                          take one of three forms:  it can be an empty
168   *                          string (to indicate that the operation should use
169   *                          anonymous authorization), a string that begins
170   *                          with "dn:" and is followed by the DN of the target
171   *                          user, or a string that begins with "u:" and is
172   *                          followed by the username for the target user
173   *                          (where the process of mapping the provided
174   *                          username to the corresponding entry will depend on
175   *                          the server configuration).  It must not be
176   *                          {@code null}.
177   */
178  public ProxiedAuthorizationV2RequestControl(
179              @NotNull final String authorizationID)
180  {
181    super(PROXIED_AUTHORIZATION_V2_REQUEST_OID, true,
182          new ASN1OctetString(authorizationID));
183
184    Validator.ensureNotNull(authorizationID);
185
186    this.authorizationID = authorizationID;
187  }
188
189
190
191  /**
192   * Creates a new proxied authorization v2 request control which is decoded
193   * from the provided generic control.
194   *
195   * @param  control  The generic control to be decoded as a proxied
196   *                  authorization v2 request control.
197   *
198   * @throws  LDAPException  If the provided control cannot be decoded as a
199   *                         proxied authorization v2 request control.
200   */
201  public ProxiedAuthorizationV2RequestControl(@NotNull final Control control)
202         throws LDAPException
203  {
204    super(control);
205
206    final ASN1OctetString value = control.getValue();
207    if (value == null)
208    {
209      throw new LDAPException(ResultCode.DECODING_ERROR,
210                              ERR_PROXY_V2_NO_VALUE.get());
211    }
212
213    authorizationID = value.stringValue();
214  }
215
216
217
218  /**
219   * Retrieves the authorization ID string that will be used to identify the
220   * user under whose authorization the associated operation should be
221   * performed.
222   *
223   * @return  The authorization ID string that will be used to identify the user
224   *          under whose authorization the associated operation should be
225   *          performed.
226   */
227  @NotNull()
228  public String getAuthorizationID()
229  {
230    return authorizationID;
231  }
232
233
234
235  /**
236   * {@inheritDoc}
237   */
238  @Override()
239  @NotNull()
240  public String getControlName()
241  {
242    return INFO_CONTROL_NAME_PROXIED_AUTHZ_V2_REQUEST.get();
243  }
244
245
246
247  /**
248   * Retrieves a representation of this proxied authorization v2 request control
249   * as a JSON object.  The JSON object uses the following fields:
250   * <UL>
251   *   <LI>
252   *     {@code oid} -- A mandatory string field whose value is the object
253   *     identifier for this control.  For the proxied authorization v2 request
254   *     control, the OID is "2.16.840.1.113730.3.4.18".
255   *   </LI>
256   *   <LI>
257   *     {@code control-name} -- An optional string field whose value is a
258   *     human-readable name for this control.  This field is only intended for
259   *     descriptive purposes, and when decoding a control, the {@code oid}
260   *     field should be used to identify the type of control.
261   *   </LI>
262   *   <LI>
263   *     {@code criticality} -- A mandatory Boolean field used to indicate
264   *     whether this control is considered critical.
265   *   </LI>
266   *   <LI>
267   *     {@code value-base64} -- An optional string field whose value is a
268   *     base64-encoded representation of the raw value for this proxied
269   *     authorization v2 request control.  Exactly one of the
270   *     {@code value-base64} and {@code value-json} fields must be present.
271   *   </LI>
272   *   <LI>
273   *     {@code value-json} -- An optional JSON object field whose value is a
274   *     user-friendly representation of the value for this proxied
275   *     authorization v2 request control.  Exactly one of the
276   *     {@code value-base64} and {@code value-json} fields must be present, and
277   *     if the {@code value-json} field is used, then it will use the following
278   *     fields:
279   *     <UL>
280   *       <LI>
281   *         {@code authorization-id} -- A mandatory string field whose value is
282   *         an authorization ID that identifies the user as whom the request
283   *         should be authorized.
284   *       </LI>
285   *     </UL>
286   *   </LI>
287   * </UL>
288   *
289   * @return  A JSON object that contains a representation of this control.
290   */
291  @Override()
292  @NotNull()
293  public JSONObject toJSONControl()
294  {
295    return new JSONObject(
296         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
297              PROXIED_AUTHORIZATION_V2_REQUEST_OID),
298         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
299              INFO_CONTROL_NAME_PROXIED_AUTHZ_V2_REQUEST.get()),
300         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
301              isCritical()),
302         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
303              new JSONObject(
304                   new JSONField(JSON_FIELD_AUTHORIZATION_ID,
305                        authorizationID))));
306  }
307
308
309
310  /**
311   * Attempts to decode the provided object as a JSON representation of a
312   * proxied authorization v2 request control.
313   *
314   * @param  controlObject  The JSON object to be decoded.  It must not be
315   *                        {@code null}.
316   * @param  strict         Indicates whether to use strict mode when decoding
317   *                        the provided JSON object.  If this is {@code true},
318   *                        then this method will throw an exception if the
319   *                        provided JSON object contains any unrecognized
320   *                        fields.  If this is {@code false}, then unrecognized
321   *                        fields will be ignored.
322   *
323   * @return  The proxied authorization v2 request control that was decoded from
324   *          the provided JSON object.
325   *
326   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
327   *                         valid proxied authorization v2 request control.
328   */
329  @NotNull()
330  public static ProxiedAuthorizationV2RequestControl decodeJSONControl(
331              @NotNull final JSONObject controlObject,
332              final boolean strict)
333         throws LDAPException
334  {
335    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
336         controlObject, strict, true, true);
337
338    final ASN1OctetString rawValue = jsonControl.getRawValue();
339    if (rawValue != null)
340    {
341      return new ProxiedAuthorizationV2RequestControl(new Control(
342           jsonControl.getOID(), jsonControl.getCriticality(), rawValue));
343    }
344
345
346    final JSONObject valueObject = jsonControl.getValueObject();
347
348    final String authorizationID =
349         valueObject.getFieldAsString(JSON_FIELD_AUTHORIZATION_ID);
350    if (authorizationID == null)
351    {
352      throw new LDAPException(ResultCode.DECODING_ERROR,
353           ERR_PROXYV2_JSON_MISSING_AUTHZ_ID.get(
354                controlObject.toSingleLineString(),
355                JSON_FIELD_AUTHORIZATION_ID));
356    }
357
358
359    if (strict)
360    {
361      final List<String> unrecognizedFields =
362           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
363                valueObject, JSON_FIELD_AUTHORIZATION_ID);
364      if (! unrecognizedFields.isEmpty())
365      {
366        throw new LDAPException(ResultCode.DECODING_ERROR,
367             ERR_PROXYV2_JSON_UNRECOGNIZED_FIELD.get(
368                  controlObject.toSingleLineString(),
369                  unrecognizedFields.get(0)));
370      }
371    }
372
373
374    return new ProxiedAuthorizationV2RequestControl(authorizationID);
375  }
376
377
378
379  /**
380   * {@inheritDoc}
381   */
382  @Override()
383  public void toString(@NotNull final StringBuilder buffer)
384  {
385    buffer.append("ProxiedAuthorizationV2RequestControl(authorizationID='");
386    buffer.append(authorizationID);
387    buffer.append("')");
388  }
389}