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 }