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}