001 /* 002 * Copyright 2010-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 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 */ 021 package com.unboundid.ldap.sdk.unboundidds.examples; 022 023 024 025 import java.io.OutputStream; 026 import java.io.Serializable; 027 import java.util.Collections; 028 import java.util.LinkedHashMap; 029 import java.util.LinkedHashSet; 030 import java.util.List; 031 import java.util.Set; 032 033 import com.unboundid.ldap.sdk.ExtendedResult; 034 import com.unboundid.ldap.sdk.LDAPConnection; 035 import com.unboundid.ldap.sdk.LDAPException; 036 import com.unboundid.ldap.sdk.ResultCode; 037 import com.unboundid.ldap.sdk.Version; 038 import com.unboundid.ldap.sdk.unboundidds.extensions. 039 GetSubtreeAccessibilityExtendedRequest; 040 import com.unboundid.ldap.sdk.unboundidds.extensions. 041 GetSubtreeAccessibilityExtendedResult; 042 import com.unboundid.ldap.sdk.unboundidds.extensions. 043 SetSubtreeAccessibilityExtendedRequest; 044 import com.unboundid.ldap.sdk.unboundidds.extensions. 045 SubtreeAccessibilityRestriction; 046 import com.unboundid.ldap.sdk.unboundidds.extensions.SubtreeAccessibilityState; 047 import com.unboundid.util.Debug; 048 import com.unboundid.util.LDAPCommandLineTool; 049 import com.unboundid.util.StaticUtils; 050 import com.unboundid.util.ThreadSafety; 051 import com.unboundid.util.ThreadSafetyLevel; 052 import com.unboundid.util.args.ArgumentException; 053 import com.unboundid.util.args.ArgumentParser; 054 import com.unboundid.util.args.BooleanArgument; 055 import com.unboundid.util.args.DNArgument; 056 import com.unboundid.util.args.StringArgument; 057 058 059 060 /** 061 * <BLOCKQUOTE> 062 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 063 * LDAP SDK for Java. It is not available for use in applications that 064 * include only the Standard Edition of the LDAP SDK, and is not supported for 065 * use in conjunction with non-UnboundID products. 066 * </BLOCKQUOTE> 067 * This class provides a utility that can be used to query and update the set of 068 * subtree accessibility restrictions defined in the Directory Server. 069 * <BR><BR> 070 * The APIs demonstrated by this example include: 071 * <UL> 072 * <LI>The use of the get/set subtree accessibility extended operations</LI> 073 * <LI>The LDAP command-line tool API.</LI> 074 * <LI>Argument parsing.</LI> 075 * </UL> 076 */ 077 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 078 public final class SubtreeAccessibility 079 extends LDAPCommandLineTool 080 implements Serializable 081 { 082 /** 083 * The set of allowed subtree accessibility state values. 084 */ 085 private static final Set<String> ALLOWED_ACCESSIBILITY_STATES; 086 static 087 { 088 final LinkedHashSet<String> stateValues = new LinkedHashSet<String>(4); 089 090 stateValues.add(SubtreeAccessibilityState.ACCESSIBLE.getStateName()); 091 stateValues.add( 092 SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED.getStateName()); 093 stateValues.add( 094 SubtreeAccessibilityState.READ_ONLY_BIND_DENIED.getStateName()); 095 stateValues.add(SubtreeAccessibilityState.HIDDEN.getStateName()); 096 097 ALLOWED_ACCESSIBILITY_STATES = Collections.unmodifiableSet(stateValues); 098 } 099 100 101 102 /** 103 * The serial version UID for this serializable class. 104 */ 105 private static final long serialVersionUID = 3703682568143472108L; 106 107 108 109 // Indicates whether the set of subtree restrictions should be updated rather 110 // than queried. 111 private BooleanArgument set; 112 113 // The argument used to specify the base DN for the target subtree. 114 private DNArgument baseDN; 115 116 // The argument used to specify the DN of a user who can bypass restrictions 117 // on the target subtree. 118 private DNArgument bypassUserDN; 119 120 // The argument used to specify the accessibility state for the target 121 // subtree. 122 private StringArgument accessibilityState; 123 124 125 126 /** 127 * Parse the provided command line arguments and perform the appropriate 128 * processing. 129 * 130 * @param args The command line arguments provided to this program. 131 */ 132 public static void main(final String[] args) 133 { 134 final ResultCode resultCode = main(args, System.out, System.err); 135 if (resultCode != ResultCode.SUCCESS) 136 { 137 System.exit(resultCode.intValue()); 138 } 139 } 140 141 142 143 /** 144 * Parse the provided command line arguments and perform the appropriate 145 * processing. 146 * 147 * @param args The command line arguments provided to this program. 148 * @param outStream The output stream to which standard out should be 149 * written. It may be {@code null} if output should be 150 * suppressed. 151 * @param errStream The output stream to which standard error should be 152 * written. It may be {@code null} if error messages 153 * should be suppressed. 154 * 155 * @return A result code indicating whether the processing was successful. 156 */ 157 public static ResultCode main(final String[] args, 158 final OutputStream outStream, 159 final OutputStream errStream) 160 { 161 final SubtreeAccessibility tool = 162 new SubtreeAccessibility(outStream, errStream); 163 return tool.runTool(args); 164 } 165 166 167 168 /** 169 * Creates a new instance of this tool. 170 * 171 * @param outStream The output stream to which standard out should be 172 * written. It may be {@code null} if output should be 173 * suppressed. 174 * @param errStream The output stream to which standard error should be 175 * written. It may be {@code null} if error messages 176 * should be suppressed. 177 */ 178 public SubtreeAccessibility(final OutputStream outStream, 179 final OutputStream errStream) 180 { 181 super(outStream, errStream); 182 183 set = null; 184 baseDN = null; 185 bypassUserDN = null; 186 accessibilityState = null; 187 } 188 189 190 191 192 /** 193 * Retrieves the name of this tool. It should be the name of the command used 194 * to invoke this tool. 195 * 196 * @return The name for this tool. 197 */ 198 @Override() 199 public String getToolName() 200 { 201 return "subtree-accessibility"; 202 } 203 204 205 206 /** 207 * Retrieves a human-readable description for this tool. 208 * 209 * @return A human-readable description for this tool. 210 */ 211 @Override() 212 public String getToolDescription() 213 { 214 return "List or update the a set of subtree accessibility restrictions " + 215 "defined in the Directory Server."; 216 } 217 218 219 220 /** 221 * Retrieves the version string for this tool. 222 * 223 * @return The version string for this tool. 224 */ 225 @Override() 226 public String getToolVersion() 227 { 228 return Version.NUMERIC_VERSION_STRING; 229 } 230 231 232 233 /** 234 * Adds the arguments needed by this command-line tool to the provided 235 * argument parser which are not related to connecting or authenticating to 236 * the directory server. 237 * 238 * @param parser The argument parser to which the arguments should be added. 239 * 240 * @throws ArgumentException If a problem occurs while adding the arguments. 241 */ 242 @Override() 243 public void addNonLDAPArguments(final ArgumentParser parser) 244 throws ArgumentException 245 { 246 set = new BooleanArgument('s', "set", 1, 247 "Indicates that the set of accessibility restrictions should be " + 248 "updated rather than retrieved."); 249 parser.addArgument(set); 250 251 252 baseDN = new DNArgument('b', "baseDN", false, 1, "{dn}", 253 "The base DN of the subtree for which an accessibility restriction " + 254 "is to be updated."); 255 parser.addArgument(baseDN); 256 257 258 accessibilityState = new StringArgument('S', "state", false, 1, "{state}", 259 "The accessibility state to use for the accessibility restriction " + 260 "on the target subtree. Allowed values: " + 261 SubtreeAccessibilityState.ACCESSIBLE.getStateName() + ", " + 262 SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED.getStateName() + 263 ", " + 264 SubtreeAccessibilityState.READ_ONLY_BIND_DENIED.getStateName() + 265 ", " + SubtreeAccessibilityState.HIDDEN.getStateName() + '.', 266 ALLOWED_ACCESSIBILITY_STATES); 267 parser.addArgument(accessibilityState); 268 269 270 bypassUserDN = new DNArgument('B', "bypassUserDN", false, 1, "{dn}", 271 "The DN of a user who is allowed to bypass restrictions on the " + 272 "target subtree."); 273 parser.addArgument(bypassUserDN); 274 275 276 // The baseDN, accessibilityState, and bypassUserDN arguments can only be 277 // used if the set argument was provided. 278 parser.addDependentArgumentSet(baseDN, set); 279 parser.addDependentArgumentSet(accessibilityState, set); 280 parser.addDependentArgumentSet(bypassUserDN, set); 281 282 283 // If the set argument was provided, then the base DN and accessibilityState 284 // arguments must also be given. 285 parser.addDependentArgumentSet(set, baseDN); 286 parser.addDependentArgumentSet(set, accessibilityState); 287 } 288 289 290 291 /** 292 * Performs the core set of processing for this tool. 293 * 294 * @return A result code that indicates whether the processing completed 295 * successfully. 296 */ 297 @Override() 298 public ResultCode doToolProcessing() 299 { 300 // Get a connection to the target directory server. 301 final LDAPConnection connection; 302 try 303 { 304 connection = getConnection(); 305 } 306 catch (final LDAPException le) 307 { 308 Debug.debugException(le); 309 err("Unable to establish a connection to the target directory server: ", 310 StaticUtils.getExceptionMessage(le)); 311 return le.getResultCode(); 312 } 313 314 try 315 { 316 // See whether to do a get or set operation and call the appropriate 317 // method. 318 if (set.isPresent()) 319 { 320 return doSet(connection); 321 } 322 else 323 { 324 return doGet(connection); 325 } 326 } 327 finally 328 { 329 connection.close(); 330 } 331 } 332 333 334 335 /** 336 * Does the work necessary to retrieve the set of subtree accessibility 337 * restrictions defined in the server. 338 * 339 * @param connection The connection to use to communicate with the server. 340 * 341 * @return A result code with information about the result of operation 342 * processing. 343 */ 344 private ResultCode doGet(final LDAPConnection connection) 345 { 346 final GetSubtreeAccessibilityExtendedResult result; 347 try 348 { 349 result = (GetSubtreeAccessibilityExtendedResult) 350 connection.processExtendedOperation( 351 new GetSubtreeAccessibilityExtendedRequest()); 352 } 353 catch (final LDAPException le) 354 { 355 Debug.debugException(le); 356 err("An error occurred while attempting to invoke the get subtree " + 357 "accessibility request: ", StaticUtils.getExceptionMessage(le)); 358 return le.getResultCode(); 359 } 360 361 if (result.getResultCode() != ResultCode.SUCCESS) 362 { 363 err("The server returned an error for the get subtree accessibility " + 364 "request: ", result.getDiagnosticMessage()); 365 return result.getResultCode(); 366 } 367 368 final List<SubtreeAccessibilityRestriction> restrictions = 369 result.getAccessibilityRestrictions(); 370 if ((restrictions == null) || restrictions.isEmpty()) 371 { 372 out("There are no subtree accessibility restrictions defined in the " + 373 "server."); 374 return ResultCode.SUCCESS; 375 } 376 377 if (restrictions.size() == 1) 378 { 379 out("1 subtree accessibility restriction was found in the server:"); 380 } 381 else 382 { 383 out(restrictions.size(), 384 " subtree accessibility restrictions were found in the server:"); 385 } 386 387 for (final SubtreeAccessibilityRestriction r : restrictions) 388 { 389 out("Subtree Base DN: ", r.getSubtreeBaseDN()); 390 out("Accessibility State: ", r.getAccessibilityState().getStateName()); 391 392 final String bypassDN = r.getBypassUserDN(); 393 if (bypassDN != null) 394 { 395 out("Bypass User DN: ", bypassDN); 396 } 397 398 out("Effective Time: ", r.getEffectiveTime()); 399 out(); 400 } 401 402 return ResultCode.SUCCESS; 403 } 404 405 406 407 /** 408 * Does the work necessary to update a subtree accessibility restriction 409 * defined in the server. 410 * 411 * @param connection The connection to use to communicate with the server. 412 * 413 * @return A result code with information about the result of operation 414 * processing. 415 */ 416 private ResultCode doSet(final LDAPConnection connection) 417 { 418 final SubtreeAccessibilityState state = 419 SubtreeAccessibilityState.forName(accessibilityState.getValue()); 420 if (state == null) 421 { 422 // This should never happen. 423 err("Unsupported subtree accessibility state ", 424 accessibilityState.getValue()); 425 return ResultCode.PARAM_ERROR; 426 } 427 428 final SetSubtreeAccessibilityExtendedRequest request; 429 switch (state) 430 { 431 case ACCESSIBLE: 432 request = SetSubtreeAccessibilityExtendedRequest. 433 createSetAccessibleRequest(baseDN.getStringValue()); 434 break; 435 case READ_ONLY_BIND_ALLOWED: 436 request = SetSubtreeAccessibilityExtendedRequest. 437 createSetReadOnlyRequest(baseDN.getStringValue(), true, 438 bypassUserDN.getStringValue()); 439 break; 440 case READ_ONLY_BIND_DENIED: 441 request = SetSubtreeAccessibilityExtendedRequest. 442 createSetReadOnlyRequest(baseDN.getStringValue(), false, 443 bypassUserDN.getStringValue()); 444 break; 445 case HIDDEN: 446 request = SetSubtreeAccessibilityExtendedRequest.createSetHiddenRequest( 447 baseDN.getStringValue(), bypassUserDN.getStringValue()); 448 break; 449 default: 450 // This should never happen. 451 err("Unsupported subtree accessibility state ", state.getStateName()); 452 return ResultCode.PARAM_ERROR; 453 } 454 455 final ExtendedResult result; 456 try 457 { 458 result = connection.processExtendedOperation(request); 459 } 460 catch (final LDAPException le) 461 { 462 Debug.debugException(le); 463 err("An error occurred while attempting to invoke the set subtree " + 464 "accessibility request: ", StaticUtils.getExceptionMessage(le)); 465 return le.getResultCode(); 466 } 467 468 if (result.getResultCode() == ResultCode.SUCCESS) 469 { 470 out("Successfully set an accessibility state of ", state.getStateName(), 471 " for subtree ", baseDN.getStringValue()); 472 } 473 else 474 { 475 out("Unable to set an accessibility state of ", state.getStateName(), 476 " for subtree ", baseDN.getStringValue(), ": ", 477 result.getDiagnosticMessage()); 478 } 479 480 return result.getResultCode(); 481 } 482 483 484 485 /** 486 * Retrieves a set of information that may be used to generate example usage 487 * information. Each element in the returned map should consist of a map 488 * between an example set of arguments and a string that describes the 489 * behavior of the tool when invoked with that set of arguments. 490 * 491 * @return A set of information that may be used to generate example usage 492 * information. It may be {@code null} or empty if no example usage 493 * information is available. 494 */ 495 @Override() 496 public LinkedHashMap<String[],String> getExampleUsages() 497 { 498 final LinkedHashMap<String[],String> exampleMap = 499 new LinkedHashMap<String[],String>(2); 500 501 final String[] getArgs = 502 { 503 "--hostname", "server.example.com", 504 "--port", "389", 505 "--bindDN", "uid=admin,dc=example,dc=com", 506 "--bindPassword", "password", 507 }; 508 exampleMap.put(getArgs, 509 "Retrieve information about all subtree accessibility restrictions " + 510 "defined in the server."); 511 512 final String[] setArgs = 513 { 514 "--hostname", "server.example.com", 515 "--port", "389", 516 "--bindDN", "uid=admin,dc=example,dc=com", 517 "--bindPassword", "password", 518 "--set", 519 "--baseDN", "ou=subtree,dc=example,dc=com", 520 "--state", "read-only-bind-allowed", 521 "--bypassUserDN", "uid=bypass,dc=example,dc=com" 522 }; 523 exampleMap.put(setArgs, 524 "Create or update the subtree accessibility state definition for " + 525 "subtree 'ou=subtree,dc=example,dc=com' so that it is " + 526 "read-only for all users except 'uid=bypass,dc=example,dc=com'."); 527 528 return exampleMap; 529 } 530 }