001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 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.NotMutable; 041import com.unboundid.util.StaticUtils; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044 045import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 046import static com.unboundid.util.Debug.*; 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 Alcatel-Lucent 8661 059 * server products. These classes provide support for proprietary 060 * functionality or for external specifications that are not considered stable 061 * or mature enough to be guaranteed to work in an interoperable way with 062 * 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<SubtreeAccessibilityRestriction>( 169 restrictionElements.length); 170 171 for (final ASN1Element e : restrictionElements) 172 { 173 String baseDN = null; 174 SubtreeAccessibilityState state = null; 175 String bypassDN = null; 176 Date effectiveTime = null; 177 178 for (final ASN1Element re : ASN1Sequence.decodeAsSequence(e).elements()) 179 { 180 switch (re.getType()) 181 { 182 case TYPE_BASE_DN: 183 baseDN = ASN1OctetString.decodeAsOctetString(re).stringValue(); 184 break; 185 case TYPE_STATE: 186 state = SubtreeAccessibilityState.valueOf( 187 ASN1Enumerated.decodeAsEnumerated(re).intValue()); 188 if (state == null) 189 { 190 throw new LDAPException(ResultCode.DECODING_ERROR, 191 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_STATE.get( 192 ASN1Enumerated.decodeAsEnumerated(re).intValue())); 193 } 194 break; 195 case TYPE_BYPASS_USER: 196 bypassDN = ASN1OctetString.decodeAsOctetString(re).stringValue(); 197 break; 198 case TYPE_EFFECTIVE_TIME: 199 effectiveTime = StaticUtils.decodeGeneralizedTime( 200 ASN1OctetString.decodeAsOctetString(re).stringValue()); 201 break; 202 default: 203 throw new LDAPException(ResultCode.DECODING_ERROR, 204 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_TYPE.get( 205 StaticUtils.toHex(re.getType()))); 206 } 207 } 208 209 if (baseDN == null) 210 { 211 throw new LDAPException(ResultCode.DECODING_ERROR, 212 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_BASE.get()); 213 } 214 215 if (state == null) 216 { 217 throw new LDAPException(ResultCode.DECODING_ERROR, 218 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_STATE.get()); 219 } 220 221 if (effectiveTime == null) 222 { 223 throw new LDAPException(ResultCode.DECODING_ERROR, 224 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_TIME.get()); 225 } 226 227 restrictionList.add(new SubtreeAccessibilityRestriction(baseDN, state, 228 bypassDN, effectiveTime)); 229 } 230 231 accessibilityRestrictions = Collections.unmodifiableList(restrictionList); 232 } 233 catch (final LDAPException le) 234 { 235 debugException(le); 236 throw le; 237 } 238 catch (final Exception e) 239 { 240 debugException(e); 241 throw new LDAPException(ResultCode.DECODING_ERROR, 242 ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_DECODE_ERROR.get( 243 StaticUtils.getExceptionMessage(e)), 244 e); 245 } 246 } 247 248 249 250 /** 251 * Creates a new get subtree accessibility extended result with the provided 252 * information. 253 * 254 * @param messageID The message ID for the LDAP message that is 255 * associated with this LDAP result. 256 * @param resultCode The result code from the response. 257 * @param diagnosticMessage The diagnostic message from the response, if 258 * available. 259 * @param matchedDN The matched DN from the response, if available. 260 * @param referralURLs The set of referral URLs from the response, if 261 * available. 262 * @param restrictions The set of subtree accessibility restrictions 263 * to include in the response. It may be 264 * {@code null} if this represents an error 265 * response, or it may be empty if there are no 266 * subtree accessibility restrictions defined in 267 * the server. 268 * @param responseControls The set of controls from the response, if 269 * available. 270 */ 271 public GetSubtreeAccessibilityExtendedResult(final int messageID, 272 final ResultCode resultCode, final String diagnosticMessage, 273 final String matchedDN, final String[] referralURLs, 274 final Collection<SubtreeAccessibilityRestriction> restrictions, 275 final Control... responseControls) 276 { 277 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 278 null, encodeValue(restrictions), responseControls); 279 280 if (restrictions == null) 281 { 282 accessibilityRestrictions = null; 283 } 284 else 285 { 286 accessibilityRestrictions = Collections.unmodifiableList( 287 new ArrayList<SubtreeAccessibilityRestriction>(restrictions)); 288 } 289 } 290 291 292 293 /** 294 * Encodes the value for this extended result using the provided information. 295 * 296 * @param restrictions The set of subtree accessibility restrictions to 297 * include in the response. It may be {@code null} if 298 * this represents an error response, or it may be empty 299 * if there are no subtree accessibility restrictions 300 * defined in the server. 301 * 302 * @return An ASN.1 octet string containing the properly-encoded value, or 303 * {@code null} if there should be no value. 304 */ 305 private static ASN1OctetString encodeValue( 306 final Collection<SubtreeAccessibilityRestriction> restrictions) 307 { 308 if (restrictions == null) 309 { 310 return null; 311 } 312 313 final ArrayList<ASN1Element> elements = 314 new ArrayList<ASN1Element>(restrictions.size()); 315 for (final SubtreeAccessibilityRestriction r : restrictions) 316 { 317 final ArrayList<ASN1Element> restrictionElements = 318 new ArrayList<ASN1Element>(4); 319 restrictionElements.add(new ASN1OctetString(TYPE_BASE_DN, 320 r.getSubtreeBaseDN())); 321 restrictionElements.add(new ASN1Enumerated(TYPE_STATE, 322 r.getAccessibilityState().intValue())); 323 324 if (r.getBypassUserDN() != null) 325 { 326 restrictionElements.add(new ASN1OctetString(TYPE_BYPASS_USER, 327 r.getBypassUserDN())); 328 } 329 330 restrictionElements.add(new ASN1OctetString(TYPE_EFFECTIVE_TIME, 331 StaticUtils.encodeGeneralizedTime(r.getEffectiveTime()))); 332 333 elements.add(new ASN1Sequence(restrictionElements)); 334 } 335 336 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 337 } 338 339 340 341 /** 342 * Retrieves a list of the subtree accessibility restrictions defined in the 343 * server. 344 * 345 * @return A list of the subtree accessibility restrictions defined in the 346 * server, an empty list if there are no restrictions defined, or 347 * {@code null} if no restriction data was included in the response 348 * from the server (e.g., because it was an error response). 349 */ 350 public List<SubtreeAccessibilityRestriction> getAccessibilityRestrictions() 351 { 352 return accessibilityRestrictions; 353 } 354 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override() 361 public String getExtendedResultName() 362 { 363 return INFO_EXTENDED_RESULT_NAME_GET_SUBTREE_ACCESSIBILITY.get(); 364 } 365 366 367 368 /** 369 * {@inheritDoc} 370 */ 371 @Override() 372 public void toString(final StringBuilder buffer) 373 { 374 buffer.append("GetSubtreeAccessibilityExtendedResult(resultCode="); 375 buffer.append(getResultCode()); 376 377 final int messageID = getMessageID(); 378 if (messageID >= 0) 379 { 380 buffer.append(", messageID="); 381 buffer.append(messageID); 382 } 383 384 final String diagnosticMessage = getDiagnosticMessage(); 385 if (diagnosticMessage != null) 386 { 387 buffer.append(", diagnosticMessage='"); 388 buffer.append(diagnosticMessage); 389 buffer.append('\''); 390 } 391 392 final String matchedDN = getMatchedDN(); 393 if (matchedDN != null) 394 { 395 buffer.append(", matchedDN='"); 396 buffer.append(matchedDN); 397 buffer.append('\''); 398 } 399 400 final String[] referralURLs = getReferralURLs(); 401 if ((referralURLs != null) && (referralURLs.length > 0)) 402 { 403 buffer.append(", referralURLs={ '"); 404 for (int i=0; i < referralURLs.length; i++) 405 { 406 if (i > 0) 407 { 408 buffer.append("', '"); 409 } 410 buffer.append(referralURLs[i]); 411 } 412 413 buffer.append("' }"); 414 } 415 416 if (accessibilityRestrictions != null) 417 { 418 buffer.append(", accessibilityRestrictions={"); 419 420 final Iterator<SubtreeAccessibilityRestriction> iterator = 421 accessibilityRestrictions.iterator(); 422 while (iterator.hasNext()) 423 { 424 iterator.next().toString(buffer); 425 if (iterator.hasNext()) 426 { 427 buffer.append(", "); 428 } 429 } 430 431 buffer.append('}'); 432 } 433 434 final Control[] controls = getResponseControls(); 435 if (controls.length > 0) 436 { 437 buffer.append(", controls={"); 438 for (int i=0; i < controls.length; i++) 439 { 440 if (i > 0) 441 { 442 buffer.append(", "); 443 } 444 445 buffer.append(controls[i]); 446 } 447 buffer.append('}'); 448 } 449 450 buffer.append(')'); 451 } 452}