001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.unboundidds.extensions; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.Date; 029import java.util.Iterator; 030import java.util.List; 031 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1Enumerated; 034import com.unboundid.asn1.ASN1OctetString; 035import com.unboundid.asn1.ASN1Sequence; 036import com.unboundid.ldap.sdk.Control; 037import com.unboundid.ldap.sdk.ExtendedResult; 038import com.unboundid.ldap.sdk.LDAPException; 039import com.unboundid.ldap.sdk.ResultCode; 040import com.unboundid.util.Debug; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.StaticUtils; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 047 048 049 050/** 051 * This class provides an implementation of an extended result that holds 052 * information about the response returned from a 053 * {@link GetSubtreeAccessibilityExtendedRequest}. 054 * <BR> 055 * <BLOCKQUOTE> 056 * <B>NOTE:</B> This class, and other classes within the 057 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 058 * supported for use against Ping Identity, UnboundID, and 059 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 060 * for proprietary functionality or for external specifications that are not 061 * considered stable or mature enough to be guaranteed to work in an 062 * interoperable way with other types of LDAP servers. 063 * </BLOCKQUOTE> 064 * <BR> 065 * It has an OID of 1.3.6.1.4.1.30221.1.6.21, and successful responses will have 066 * a value with the following encoding: 067 * <BR><BR> 068 * <PRE> 069 * GetSubtreeAccessibilityResultValue ::= SEQUENCE OF SEQUENCE { 070 * subtreeBaseDN [0] LDAPDN, 071 * subtreeAccessibility [1] ENUMERATED { 072 * accessible (0), 073 * read-only-bind-allowed (1), 074 * read-only-bind-denied (2), 075 * hidden (3), 076 * ... }, 077 * bypassUserDN [2] LDAPDN OPTIONAL, 078 * effectiveTime [3] OCTET STRING, 079 * ... } 080 * </PRE> 081 */ 082@NotMutable() 083@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 084public final class GetSubtreeAccessibilityExtendedResult 085 extends ExtendedResult 086{ 087 /** 088 * The OID (1.3.6.1.4.1.30221.1.6.21) for the get subtree accessibility 089 * extended result. 090 */ 091 public static final String GET_SUBTREE_ACCESSIBILITY_RESULT_OID = 092 "1.3.6.1.4.1.30221.1.6.21"; 093 094 095 096 /** 097 * The BER type for the element that holds the base DN for a subtree 098 * accessibility restriction. 099 */ 100 private static final byte TYPE_BASE_DN = (byte) 0x80; 101 102 103 104 /** 105 * The BER type for the element that holds the accessibility state for a 106 * subtree accessibility restriction. 107 */ 108 private static final byte TYPE_STATE = (byte) 0x81; 109 110 111 112 /** 113 * The BER type for the element that holds the bypass user DN for a subtree 114 * accessibility restriction. 115 */ 116 private static final byte TYPE_BYPASS_USER = (byte) 0x82; 117 118 119 120 /** 121 * The BER type for the element that holds the effective time for a subtree 122 * accessibility restriction. 123 */ 124 private static final byte TYPE_EFFECTIVE_TIME = (byte) 0x83; 125 126 127 128 /** 129 * The serial version UID for this serializable class. 130 */ 131 private static final long serialVersionUID = -3163306122775326749L; 132 133 134 135 // A list of the subtree accessibility restrictions defined in the server. 136 private final List<SubtreeAccessibilityRestriction> accessibilityRestrictions; 137 138 139 140 /** 141 * Creates a new get subtree accessibility extended result from the provided 142 * generic extended result. 143 * 144 * @param extendedResult The generic extended result to be decoded. 145 * 146 * @throws LDAPException If a problem occurs while attempting to decode the 147 * provided extended result as a get connection ID 148 * result. 149 */ 150 public GetSubtreeAccessibilityExtendedResult( 151 final ExtendedResult extendedResult) 152 throws LDAPException 153 { 154 super(extendedResult); 155 156 final ASN1OctetString value = extendedResult.getValue(); 157 if (value == null) 158 { 159 accessibilityRestrictions = null; 160 return; 161 } 162 163 try 164 { 165 final ASN1Element[] restrictionElements = 166 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 167 final ArrayList<SubtreeAccessibilityRestriction> restrictionList = 168 new ArrayList<>(restrictionElements.length); 169 170 for (final ASN1Element e : restrictionElements) 171 { 172 String baseDN = null; 173 SubtreeAccessibilityState state = null; 174 String bypassDN = null; 175 Date effectiveTime = null; 176 177 for (final ASN1Element re : ASN1Sequence.decodeAsSequence(e).elements()) 178 { 179 switch (re.getType()) 180 { 181 case TYPE_BASE_DN: 182 baseDN = ASN1OctetString.decodeAsOctetString(re).stringValue(); 183 break; 184 case TYPE_STATE: 185 state = SubtreeAccessibilityState.valueOf( 186 ASN1Enumerated.decodeAsEnumerated(re).intValue()); 187 if (state == null) 188 { 189 throw new LDAPException(ResultCode.DECODING_ERROR, 190 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_STATE.get( 191 ASN1Enumerated.decodeAsEnumerated(re).intValue())); 192 } 193 break; 194 case TYPE_BYPASS_USER: 195 bypassDN = ASN1OctetString.decodeAsOctetString(re).stringValue(); 196 break; 197 case TYPE_EFFECTIVE_TIME: 198 effectiveTime = StaticUtils.decodeGeneralizedTime( 199 ASN1OctetString.decodeAsOctetString(re).stringValue()); 200 break; 201 default: 202 throw new LDAPException(ResultCode.DECODING_ERROR, 203 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_TYPE.get( 204 StaticUtils.toHex(re.getType()))); 205 } 206 } 207 208 if (baseDN == null) 209 { 210 throw new LDAPException(ResultCode.DECODING_ERROR, 211 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_BASE.get()); 212 } 213 214 if (state == null) 215 { 216 throw new LDAPException(ResultCode.DECODING_ERROR, 217 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_STATE.get()); 218 } 219 220 if (effectiveTime == null) 221 { 222 throw new LDAPException(ResultCode.DECODING_ERROR, 223 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_TIME.get()); 224 } 225 226 restrictionList.add(new SubtreeAccessibilityRestriction(baseDN, state, 227 bypassDN, effectiveTime)); 228 } 229 230 accessibilityRestrictions = Collections.unmodifiableList(restrictionList); 231 } 232 catch (final LDAPException le) 233 { 234 Debug.debugException(le); 235 throw le; 236 } 237 catch (final Exception e) 238 { 239 Debug.debugException(e); 240 throw new LDAPException(ResultCode.DECODING_ERROR, 241 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_DECODE_ERROR.get( 242 StaticUtils.getExceptionMessage(e)), 243 e); 244 } 245 } 246 247 248 249 /** 250 * Creates a new get subtree accessibility extended result with the provided 251 * information. 252 * 253 * @param messageID The message ID for the LDAP message that is 254 * associated with this LDAP result. 255 * @param resultCode The result code from the response. 256 * @param diagnosticMessage The diagnostic message from the response, if 257 * available. 258 * @param matchedDN The matched DN from the response, if available. 259 * @param referralURLs The set of referral URLs from the response, if 260 * available. 261 * @param restrictions The set of subtree accessibility restrictions 262 * to include in the response. It may be 263 * {@code null} if this represents an error 264 * response, or it may be empty if there are no 265 * subtree accessibility restrictions defined in 266 * the server. 267 * @param responseControls The set of controls from the response, if 268 * available. 269 */ 270 public GetSubtreeAccessibilityExtendedResult(final int messageID, 271 final ResultCode resultCode, final String diagnosticMessage, 272 final String matchedDN, final String[] referralURLs, 273 final Collection<SubtreeAccessibilityRestriction> restrictions, 274 final Control... responseControls) 275 { 276 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 277 null, encodeValue(restrictions), responseControls); 278 279 if (restrictions == null) 280 { 281 accessibilityRestrictions = null; 282 } 283 else 284 { 285 accessibilityRestrictions = Collections.unmodifiableList( 286 new ArrayList<>(restrictions)); 287 } 288 } 289 290 291 292 /** 293 * Encodes the value for this extended result using the provided information. 294 * 295 * @param restrictions The set of subtree accessibility restrictions to 296 * include in the response. It may be {@code null} if 297 * this represents an error response, or it may be empty 298 * if there are no subtree accessibility restrictions 299 * defined in the server. 300 * 301 * @return An ASN.1 octet string containing the properly-encoded value, or 302 * {@code null} if there should be no value. 303 */ 304 private static ASN1OctetString encodeValue( 305 final Collection<SubtreeAccessibilityRestriction> restrictions) 306 { 307 if (restrictions == null) 308 { 309 return null; 310 } 311 312 final ArrayList<ASN1Element> elements = 313 new ArrayList<>(restrictions.size()); 314 for (final SubtreeAccessibilityRestriction r : restrictions) 315 { 316 final ArrayList<ASN1Element> restrictionElements = new ArrayList<>(4); 317 restrictionElements.add(new ASN1OctetString(TYPE_BASE_DN, 318 r.getSubtreeBaseDN())); 319 restrictionElements.add(new ASN1Enumerated(TYPE_STATE, 320 r.getAccessibilityState().intValue())); 321 322 if (r.getBypassUserDN() != null) 323 { 324 restrictionElements.add(new ASN1OctetString(TYPE_BYPASS_USER, 325 r.getBypassUserDN())); 326 } 327 328 restrictionElements.add(new ASN1OctetString(TYPE_EFFECTIVE_TIME, 329 StaticUtils.encodeGeneralizedTime(r.getEffectiveTime()))); 330 331 elements.add(new ASN1Sequence(restrictionElements)); 332 } 333 334 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 335 } 336 337 338 339 /** 340 * Retrieves a list of the subtree accessibility restrictions defined in the 341 * server. 342 * 343 * @return A list of the subtree accessibility restrictions defined in the 344 * server, an empty list if there are no restrictions defined, or 345 * {@code null} if no restriction data was included in the response 346 * from the server (e.g., because it was an error response). 347 */ 348 public List<SubtreeAccessibilityRestriction> getAccessibilityRestrictions() 349 { 350 return accessibilityRestrictions; 351 } 352 353 354 355 /** 356 * {@inheritDoc} 357 */ 358 @Override() 359 public String getExtendedResultName() 360 { 361 return INFO_EXTENDED_RESULT_NAME_GET_SUBTREE_ACCESSIBILITY.get(); 362 } 363 364 365 366 /** 367 * {@inheritDoc} 368 */ 369 @Override() 370 public void toString(final StringBuilder buffer) 371 { 372 buffer.append("GetSubtreeAccessibilityExtendedResult(resultCode="); 373 buffer.append(getResultCode()); 374 375 final int messageID = getMessageID(); 376 if (messageID >= 0) 377 { 378 buffer.append(", messageID="); 379 buffer.append(messageID); 380 } 381 382 final String diagnosticMessage = getDiagnosticMessage(); 383 if (diagnosticMessage != null) 384 { 385 buffer.append(", diagnosticMessage='"); 386 buffer.append(diagnosticMessage); 387 buffer.append('\''); 388 } 389 390 final String matchedDN = getMatchedDN(); 391 if (matchedDN != null) 392 { 393 buffer.append(", matchedDN='"); 394 buffer.append(matchedDN); 395 buffer.append('\''); 396 } 397 398 final String[] referralURLs = getReferralURLs(); 399 if ((referralURLs != null) && (referralURLs.length > 0)) 400 { 401 buffer.append(", referralURLs={ '"); 402 for (int i=0; i < referralURLs.length; i++) 403 { 404 if (i > 0) 405 { 406 buffer.append("', '"); 407 } 408 buffer.append(referralURLs[i]); 409 } 410 411 buffer.append("' }"); 412 } 413 414 if (accessibilityRestrictions != null) 415 { 416 buffer.append(", accessibilityRestrictions={"); 417 418 final Iterator<SubtreeAccessibilityRestriction> iterator = 419 accessibilityRestrictions.iterator(); 420 while (iterator.hasNext()) 421 { 422 iterator.next().toString(buffer); 423 if (iterator.hasNext()) 424 { 425 buffer.append(", "); 426 } 427 } 428 429 buffer.append('}'); 430 } 431 432 final Control[] controls = getResponseControls(); 433 if (controls.length > 0) 434 { 435 buffer.append(", controls={"); 436 for (int i=0; i < controls.length; i++) 437 { 438 if (i > 0) 439 { 440 buffer.append(", "); 441 } 442 443 buffer.append(controls[i]); 444 } 445 buffer.append('}'); 446 } 447 448 buffer.append(')'); 449 } 450}