All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.tools.LDAPCompare Maven / Gradle / Ivy

/*
 * Copyright 2020-2021 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2020-2021 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2020-2021 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.tools;



import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.ldap.sdk.CompareRequest;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldap.sdk.controls.AssertionRequestControl;
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetAuthorizationEntryRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetUserResourceLimitsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            OperationPurposeRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            PasswordPolicyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionPostConnectProcessor;
import com.unboundid.util.Base64;
import com.unboundid.util.DNFileReader;
import com.unboundid.util.Debug;
import com.unboundid.util.LDAPCommandLineTool;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.ObjectPair;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.ControlArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.FileArgument;
import com.unboundid.util.args.FilterArgument;
import com.unboundid.util.args.StringArgument;

import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;



/**
 * This class provide an LDAP command-line tool that may be used to perform
 * compare operations in an LDAP directory server.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class LDAPCompare extends LDAPCommandLineTool implements UnsolicitedNotificationHandler { /** * The column at which output should be wrapped. */ private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; /** * The value used to select the CSV output format. */ @NotNull private static final String OUTPUT_FORMAT_CSV = "csv"; /** * The value used to select the JSON output format. */ @NotNull private static final String OUTPUT_FORMAT_JSON = "json"; /** * The value used to select the tab-delimited output format. */ @NotNull private static final String OUTPUT_FORMAT_TAB_DELIMITED = "tab-delimited"; // A reference to the completion message to return for this tool. @NotNull private final AtomicReference completionMessage; // A reference to the argument parser for this tool. @Nullable private ArgumentParser argumentParser; // The supported command-line arguments. @Nullable private BooleanArgument authorizationIdentity; @Nullable private BooleanArgument continueOnError; @Nullable private BooleanArgument dryRun; @Nullable private BooleanArgument followReferrals; @Nullable private BooleanArgument getUserResourceLimits; @Nullable private BooleanArgument manageDsaIT; @Nullable private BooleanArgument scriptFriendly; @Nullable private BooleanArgument teeOutput; @Nullable private BooleanArgument terse; @Nullable private BooleanArgument useAdministrativeSession; @Nullable private BooleanArgument useCompareResultCodeAsExitCode; @Nullable private BooleanArgument usePasswordPolicyControl; @Nullable private BooleanArgument verbose; @Nullable private ControlArgument bindControl; @Nullable private ControlArgument compareControl; @Nullable private DNArgument proxyV1As; @Nullable private FileArgument assertionFile; @Nullable private FileArgument dnFile; @Nullable private FileArgument outputFile; @Nullable private FilterArgument assertionFilter; @Nullable private StringArgument getAuthorizationEntryAttribute; @Nullable private StringArgument operationPurpose; @Nullable private StringArgument outputFormat; @Nullable private StringArgument proxyAs; /** * Invokes this tool with the provided set of arguments. The default standard * output and error streams will be used. * * @param args The command-line arguments provided to this program. */ public static void main(@NotNull final String... args) { final ResultCode resultCode = main(System.out, System.err, args); if (resultCode != ResultCode.SUCCESS) { System.exit(resultCode.intValue()); } } /** * Invokes this tool with the provided set of arguments, and using the * provided streams for standard output and error. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. * @param args The command-line arguments provided to this program. * * @return The result code obtained when running the tool. Any result code * other than {@link ResultCode#SUCCESS} indicates an error. */ @NotNull() public static ResultCode main(@Nullable final OutputStream out, @Nullable final OutputStream err, @NotNull final String... args) { final LDAPCompare tool = new LDAPCompare(out, err); return tool.runTool(args); } /** * Creates a new instance of this tool with the provided output and error * streams. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. */ public LDAPCompare(@Nullable final OutputStream out, @Nullable final OutputStream err) { super(out, err); completionMessage = new AtomicReference<>(); argumentParser = null; authorizationIdentity = null; continueOnError = null; dryRun = null; followReferrals = null; getUserResourceLimits = null; manageDsaIT = null; scriptFriendly = null; teeOutput = null; terse = null; useAdministrativeSession = null; useCompareResultCodeAsExitCode = null; usePasswordPolicyControl = null; verbose = null; bindControl = null; compareControl = null; proxyV1As = null; assertionFile = null; dnFile = null; outputFile = null; assertionFilter = null; getAuthorizationEntryAttribute = null; operationPurpose = null; outputFormat = null; proxyAs = null; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolName() { return "ldapcompare"; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolDescription() { return INFO_LDAPCOMPARE_TOOL_DESCRIPTION_1.get(); } /** * {@inheritDoc} */ @Override() @NotNull() public List getAdditionalDescriptionParagraphs() { return Collections.unmodifiableList(Arrays.asList( INFO_LDAPCOMPARE_TOOL_DESCRIPTION_2.get(), INFO_LDAPCOMPARE_TOOL_DESCRIPTION_3.get(), INFO_LDAPCOMPARE_TOOL_DESCRIPTION_4.get(), INFO_LDAPCOMPARE_TOOL_DESCRIPTION_5.get())); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolVersion() { return Version.NUMERIC_VERSION_STRING; } /** * {@inheritDoc} */ @Override() public int getMinTrailingArguments() { return 0; } /** * {@inheritDoc} */ @Override() public int getMaxTrailingArguments() { return -1; } /** * {@inheritDoc} */ @Override() @NotNull() public String getTrailingArgumentsPlaceholder() { return INFO_LDAPCOMPARE_TRAILING_ARGS_PLACEHOLDER.get(); } /** * {@inheritDoc} */ @Override() public boolean supportsInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean defaultsToInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean supportsPropertiesFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsAuthentication() { return true; } /** * {@inheritDoc} */ @Override() protected boolean defaultToPromptForBindPassword() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsSASLHelp() { return true; } /** * {@inheritDoc} */ @Override() protected boolean includeAlternateLongIdentifiers() { return true; } /** * {@inheritDoc} */ @Override() @NotNull() protected List getBindControls() { final List bindControls = new ArrayList<>(10); if (bindControl.isPresent()) { bindControls.addAll(bindControl.getValues()); } if (authorizationIdentity.isPresent()) { bindControls.add(new AuthorizationIdentityRequestControl(false)); } if (getAuthorizationEntryAttribute.isPresent()) { bindControls.add(new GetAuthorizationEntryRequestControl(true, true, getAuthorizationEntryAttribute.getValues())); } if (getUserResourceLimits.isPresent()) { bindControls.add(new GetUserResourceLimitsRequestControl()); } if (usePasswordPolicyControl.isPresent()) { bindControls.add(new PasswordPolicyRequestControl()); } return bindControls; } /** * {@inheritDoc} */ @Override() protected boolean supportsMultipleServers() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsSSLDebugging() { return true; } /** * {@inheritDoc} */ @Override() @NotNull() public LDAPConnectionOptions getConnectionOptions() { final LDAPConnectionOptions options = new LDAPConnectionOptions(); options.setUseSynchronousMode(true); options.setFollowReferrals(followReferrals.isPresent()); options.setUnsolicitedNotificationHandler(this); options.setResponseTimeoutMillis(0L); return options; } /** * {@inheritDoc} */ @Override() protected boolean logToolInvocationByDefault() { return false; } /** * {@inheritDoc} */ @Override() @Nullable() protected String getToolCompletionMessage() { return completionMessage.get(); } /** * {@inheritDoc} */ @Override() public void addNonLDAPArguments(@NotNull final ArgumentParser parser) throws ArgumentException { argumentParser = parser; // Compare operation processing arguments. dnFile = new FileArgument('f', "dnFile", false, 1, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_DN_FILE.get(), true, true, true, false); dnFile.addLongIdentifier("dn-file", true); dnFile.addLongIdentifier("filename", true); dnFile.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(dnFile); assertionFile = new FileArgument(null, "assertionFile", false, 1, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_ASSERTION_FILE.get(), true, true, true, false); assertionFile.addLongIdentifier("assertion-file", true); assertionFile.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(assertionFile); followReferrals = new BooleanArgument(null, "followReferrals", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); followReferrals.addLongIdentifier("follow-referrals", true); followReferrals.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(followReferrals); useAdministrativeSession = new BooleanArgument(null, "useAdministrativeSession", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); useAdministrativeSession.addLongIdentifier("use-administrative-session", true); useAdministrativeSession.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(useAdministrativeSession); continueOnError = new BooleanArgument('c', "continueOnError", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); continueOnError.addLongIdentifier("continue-on-error", true); continueOnError.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(continueOnError); dryRun = new BooleanArgument('n', "dryRun", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_DRY_RUN.get()); dryRun.addLongIdentifier("dry-run", true); dryRun.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_PROCESSING.get()); parser.addArgument(dryRun); // Bind control arguments. bindControl = new ControlArgument(null, "bindControl", false, 0, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_BIND_CONTROL.get()); bindControl.addLongIdentifier("bind-control", true); bindControl.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_BIND_CONTROLS.get()); parser.addArgument(bindControl); authorizationIdentity = new BooleanArgument('E', "authorizationIdentity", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); authorizationIdentity.addLongIdentifier("authorization-identity", true); authorizationIdentity.addLongIdentifier("useAuthorizationIdentity", true); authorizationIdentity.addLongIdentifier("use-authorization-identity", true); authorizationIdentity.addLongIdentifier("useAuthorizationIdentityControl", true); authorizationIdentity.addLongIdentifier( "use-authorization-identity-control", true); authorizationIdentity.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_BIND_CONTROLS.get()); parser.addArgument(authorizationIdentity); usePasswordPolicyControl = new BooleanArgument(null, "usePasswordPolicyControl", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_USE_PW_POLICY_CONTROL.get()); usePasswordPolicyControl.addLongIdentifier("use-password-policy-control", true); usePasswordPolicyControl.addLongIdentifier("passwordPolicyControl", true); usePasswordPolicyControl.addLongIdentifier("password-policy-control", true); usePasswordPolicyControl.addLongIdentifier("passwordPolicy", true); usePasswordPolicyControl.addLongIdentifier("password-policy", true); usePasswordPolicyControl.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_BIND_CONTROLS.get()); parser.addArgument(usePasswordPolicyControl); getAuthorizationEntryAttribute = new StringArgument(null, "getAuthorizationEntryAttribute", false, 0, INFO_LDAPCOMPARE_ARG_PLACEHOLDER_ATTRIBUTE.get(), INFO_LDAPCOMPARE_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); getAuthorizationEntryAttribute.addLongIdentifier( "get-authorization-entry-attribute", true); getAuthorizationEntryAttribute.addLongIdentifier("getAuthzEntryAttribute", true); getAuthorizationEntryAttribute.addLongIdentifier( "get-authz-entry-attribute", true); getAuthorizationEntryAttribute.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_BIND_CONTROLS.get()); parser.addArgument(getAuthorizationEntryAttribute); getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 1, INFO_LDAPCOMPARE_ARG_PLACEHOLDER_GET_USER_RESOURCE_LIMITS.get()); getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); getUserResourceLimits.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_BIND_CONTROLS.get()); parser.addArgument(getUserResourceLimits); // Compare control arguments. compareControl = new ControlArgument('J', "compareControl", false, 0, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_COMPARE_CONTROL.get()); compareControl.addLongIdentifier("compare-control", true); compareControl.addLongIdentifier("control", true); compareControl.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(compareControl); proxyAs = new StringArgument('Y', "proxyAs", false, 1, INFO_LDAPCOMPARE_ARG_PLACEHOLDER_AUTHZ_ID.get(), INFO_LDAPCOMPARE_ARG_DESCRIPTION_PROXY_AS.get()); proxyAs.addLongIdentifier("proxy-as", true); proxyAs.addLongIdentifier("proxyV2As", true); proxyAs.addLongIdentifier("proxy-v2-as", true); proxyAs.addLongIdentifier("proxyV2", true); proxyAs.addLongIdentifier("proxy-v2", true); proxyAs.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(proxyAs); proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_PROXY_V1_AS.get()); proxyV1As.addLongIdentifier("proxy-v1-as", true); proxyV1As.addLongIdentifier("proxyV1", true); proxyV1As.addLongIdentifier("proxy-v1", true); proxyV1As.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(proxyV1As); manageDsaIT = new BooleanArgument(null, "manageDsaIT", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); manageDsaIT.addLongIdentifier("manage-dsa-it", true); manageDsaIT.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(manageDsaIT); assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_ASSERTION_FILTER.get()); assertionFilter.addLongIdentifier("assertion-filter", true); assertionFilter.addLongIdentifier("assertionControlFilter", true); assertionFilter.addLongIdentifier("assertion-control-filter", true); assertionFilter.addLongIdentifier("useAssertionControl", true); assertionFilter.addLongIdentifier("use-assertion-control", true); assertionFilter.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(assertionFilter); operationPurpose = new StringArgument(null, "operationPurpose", false, 1, INFO_LDAPCOMPARE_ARG_PLACEHOLDER_PURPOSE.get(), INFO_LDAPCOMPARE_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); operationPurpose.addLongIdentifier("operation-purpose", true); operationPurpose.addLongIdentifier("purpose", true); operationPurpose.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_COMPARE_CONTROLS.get()); parser.addArgument(operationPurpose); // Output Arguments. outputFile = new FileArgument(null, "outputFile", false, 1, null, INFO_LDAPCOMPARE_ARG_DESCRIPTION_OUTPUT_FILE.get(), false, true, true, false); outputFile.addLongIdentifier("output-file", true); outputFile.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(outputFile); teeOutput = new BooleanArgument(null, "teeOutput", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_TEE_OUTPUT.get()); teeOutput.addLongIdentifier("tee-output", true); teeOutput.addLongIdentifier("tee", true); teeOutput.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(teeOutput); outputFormat = new StringArgument(null, "outputFormat", false, 1, INFO_LDAPCOMPARE_ARG_PLACEHOLDER_FORMAT.get(), INFO_LDAPCOMPARE_ARG_DESCRIPTION_OUTPUT_FORMAT.get(), StaticUtils.setOf( OUTPUT_FORMAT_TAB_DELIMITED, OUTPUT_FORMAT_CSV, OUTPUT_FORMAT_JSON), OUTPUT_FORMAT_TAB_DELIMITED); outputFormat.addLongIdentifier("output-format", true); outputFormat.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(outputFormat); scriptFriendly = new BooleanArgument(null, "scriptFriendly", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get()); scriptFriendly.addLongIdentifier("script-friendly", true); scriptFriendly.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); scriptFriendly.setHidden(true); parser.addArgument(scriptFriendly); verbose = new BooleanArgument('v', "verbose", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_VERBOSE.get()); verbose.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(verbose); terse = new BooleanArgument(null, "terse", 1, INFO_LDAPCOMPARE_ARG_DESCRIPTION_TERSE.get()); terse.setArgumentGroupName(INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(terse); useCompareResultCodeAsExitCode = new BooleanArgument(null, "useCompareResultCodeAsExitCode", 1, INFO_LDAPCOMPARE_ARG_DESC_USE_COMPARE_RESULT_CODE_AS_EXIT_CODE.get()); useCompareResultCodeAsExitCode.addLongIdentifier( "use-compare-result-code-as-exit-code", true); useCompareResultCodeAsExitCode.addLongIdentifier( "useCompareResultCode", true); useCompareResultCodeAsExitCode.addLongIdentifier( "use-compare-result-code", true); useCompareResultCodeAsExitCode.setArgumentGroupName( INFO_LDAPCOMPARE_ARG_GROUP_OUTPUT.get()); parser.addArgument(useCompareResultCodeAsExitCode); parser.addExclusiveArgumentSet(dnFile, assertionFile); parser.addExclusiveArgumentSet(proxyAs, proxyV1As); parser.addDependentArgumentSet(teeOutput, outputFile); parser.addExclusiveArgumentSet(verbose, terse); } /** * {@inheritDoc} */ @Override() @NotNull() public ResultCode doToolProcessing() { final List compareRequests; try { compareRequests = getCompareRequests(); } catch (final LDAPException e) { Debug.debugException(e); logCompletionMessage(true, e.getMessage()); return e.getResultCode(); } LDAPConnectionPool pool = null; try { // Create a connection pool that will be used to communicate with the // directory server. If we should use an administrative session, then // create a connect processor that will be used to start the session // before performing the bind. try { final StartAdministrativeSessionPostConnectProcessor p; if (useAdministrativeSession.isPresent()) { p = new StartAdministrativeSessionPostConnectProcessor( new StartAdministrativeSessionExtendedRequest(getToolName(), true)); } else { p = null; } pool = getConnectionPool(1, 2, 0, p, null, true, new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, verbose.isPresent())); pool.setRetryFailedOperationsDueToInvalidConnections(true); final PrintStream writer; if (outputFile.isPresent()) { try { writer = new PrintStream(outputFile.getValue()); } catch (final Exception e) { Debug.debugException(e); logCompletionMessage(true, ERR_LDAPCOMPARE_CANNOT_OPEN_OUTPUT_FILE.get( outputFile.getValue().getAbsolutePath(), StaticUtils.getExceptionMessage(e))); return ResultCode.LOCAL_ERROR; } } else { writer = null; } final LDAPCompareOutputHandler outputHandler; switch (StaticUtils.toLowerCase(outputFormat.getValue())) { case OUTPUT_FORMAT_CSV: outputHandler = new LDAPCompareCSVOutputHandler(); break; case OUTPUT_FORMAT_JSON: outputHandler = new LDAPCompareJSONOutputHandler(); break; case OUTPUT_FORMAT_TAB_DELIMITED: default: outputHandler = new LDAPCompareTabDelimitedOutputHandler(); break; } if (! terse.isPresent()) { for (final String line : outputHandler.getHeaderLines()) { if (writer != null) { writer.println(line); } if ((writer == null) || teeOutput.isPresent()) { out(line); } } } ResultCode resultCode = null; int numTrue = 0; int numFalse = 0; int numErrors = 0; for (final CompareRequest compareRequest : compareRequests) { LDAPResult compareResult; try { compareResult = pool.compare(compareRequest); } catch (final LDAPException e) { Debug.debugException(e); compareResult = e.toLDAPResult(); } try { writeResult(writer, outputHandler, compareRequest, compareResult); } catch (final LDAPException e) { Debug.debugException(e); logCompletionMessage(true, e.getMessage()); return e.getResultCode(); } final ResultCode compareResultCode = compareResult.getResultCode(); if (compareResultCode == ResultCode.COMPARE_TRUE) { numTrue++; if (resultCode == null) { resultCode = ResultCode.COMPARE_TRUE; } } else if (compareResultCode == ResultCode.COMPARE_FALSE) { numFalse++; if (resultCode == null) { resultCode = ResultCode.COMPARE_FALSE; } } else { numErrors++; if ((resultCode == null) || (resultCode == ResultCode.COMPARE_TRUE) || (resultCode == ResultCode.COMPARE_FALSE)) { resultCode = compareResultCode; } if (! continueOnError.isPresent()) { return resultCode; } } } if (resultCode == ResultCode.COMPARE_TRUE) { if (compareRequests.size() > 1) { resultCode = ResultCode.SUCCESS; logCompletionMessage(false, INFO_LDAPCOMPARE_RESULT_ALL_SUCCEEDED.get(numTrue, numFalse)); } else { if (! useCompareResultCodeAsExitCode.isPresent()) { resultCode = ResultCode.SUCCESS; } logCompletionMessage(false, INFO_LDAPCOMPARE_RESULT_COMPARE_MATCHED.get()); } } else if (resultCode == ResultCode.COMPARE_FALSE) { if (compareRequests.size() > 1) { resultCode = ResultCode.SUCCESS; logCompletionMessage(false, INFO_LDAPCOMPARE_RESULT_ALL_SUCCEEDED.get(numTrue, numFalse)); } else { if (! useCompareResultCodeAsExitCode.isPresent()) { resultCode = ResultCode.SUCCESS; } logCompletionMessage(false, INFO_LDAPCOMPARE_RESULT_COMPARE_DID_NOT_MATCH.get()); } } else { if (compareRequests.size() > 1) { logCompletionMessage(true, ERR_LDAPCOMPARE_RESULT_WITH_ERRORS.get(numErrors, numTrue, numFalse)); } else { logCompletionMessage(true, ERR_LDAPCOMPARE_RESULT_FAILED.get()); } } return resultCode; } catch (final LDAPException le) { Debug.debugException(le); // Unable to create the connection pool, which means that either the // connection could not be established or the attempt to authenticate // the connection failed. If the bind failed, then the report bind // result health check should have already reported the bind failure. // If the failure was something else, then display that failure result. if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) { for (final String line : ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) { err(line); } } return le.getResultCode(); } } finally { if (pool != null) { pool.close(); } } } /** * Retrieves a list of the compare requests that should be issued. * * @return A list of the compare requests that should be issued. * * @throws LDAPException If a problem occurs while obtaining the compare * requests to process. */ @NotNull() private List getCompareRequests() throws LDAPException { final List trailingArgs = argumentParser.getTrailingArguments(); final int numTrailingArgs = trailingArgs.size(); if (assertionFile.isPresent()) { if (numTrailingArgs != 0) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_TRAILING_ARGS_WITH_ASSERTION_FILE.get( assertionFile.getIdentifierString())); } return readAssertionFile(getCompareControls()); } else if (dnFile.isPresent()) { if (numTrailingArgs != 1) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_INVALID_TRAILING_ARG_COUNT_WITH_DN_FILE.get( dnFile.getIdentifierString())); } final ObjectPair ava = parseAttributeValueAssertion(trailingArgs.get(0)); return readDNFile(ava.getFirst(), ava.getSecond(), getCompareControls()); } else { if (numTrailingArgs < 2) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_INVALID_TRAILING_ARG_COUNT_WITHOUT_FILE.get( dnFile.getIdentifierString(), assertionFile.getIdentifierString())); } final Iterator trailingArgsIterator = trailingArgs.iterator(); final ObjectPair ava = parseAttributeValueAssertion(trailingArgsIterator.next()); final String attributeName = ava.getFirst(); final byte[] assertionValue = ava.getSecond(); final Control[] controls = getCompareControls(); final List requests = new ArrayList<>(numTrailingArgs-1); while (trailingArgsIterator.hasNext()) { final String dnString = trailingArgsIterator.next(); try { new DN(dnString); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_MALFORMED_TRAILING_ARG_DN.get(dnString, e.getMessage()), e); } requests.add(new CompareRequest(dnString, attributeName, assertionValue, controls)); } return requests; } } /** * Parses the provided string as an attribute value assertion. It must * start with an attribute name or OID, and that must be followed by either a * single colon and the string representation of the assertion value, or * two colons and the base64-encoded representation of the assertion value. * * @param avaString The string to parse as an attribute value assertion. It * must not be {@code null}. * * @return An object pair in which the first element is the parsed attribute * name or OID, and the second element is the parsed assertion value. * * @throws LDAPException If the provided string cannot be parsed as a valid * attribute value assertion. */ @NotNull() private static ObjectPair parseAttributeValueAssertion( @NotNull final String avaString) throws LDAPException { final int colonPos = avaString.indexOf(':'); if (colonPos < 0) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_AVA_NO_COLON.get(avaString)); } else if (colonPos == 0) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_AVA_NO_ATTR.get(avaString)); } final String attributeName = avaString.substring(0, colonPos); if (colonPos == (avaString.length() - 1)) { // This means that the assertion value is empty. return new ObjectPair<>(attributeName, StaticUtils.NO_BYTES); } if (avaString.charAt(colonPos+1) == ':') { // This means that the assertion value is base64-encoded. try { final byte[] avaBytes = Base64.decode(avaString.substring(colonPos+2)); return new ObjectPair<>(attributeName, avaBytes); } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_AVA_CANNOT_BASE64_DECODE_VALUE.get(avaString, e.getMessage()), e); } } else if (avaString.charAt(colonPos+1) == '<') { // This means that the assertion value should be read from a file. The // path to that file should immediately follow the less-than symbol, and // the exact bytes contained in that file (including line breaks) will be // used as the assertion value. final String path = avaString.substring(colonPos+2); final File file = new File(path); if (file.exists()) { try { final byte[] fileBytes = StaticUtils.readFileBytes(file); return new ObjectPair<>(attributeName, fileBytes); } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_LDAPCOMPARE_AVA_CANNOT_READ_FILE.get(avaString, file.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } else { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_AVA_NO_SUCH_FILE.get(avaString, file.getAbsolutePath())); } } return new ObjectPair<>(attributeName, StaticUtils.getBytes(avaString.substring(colonPos+1))); } /** * Reads the compare requests to process from the information in the * specified assertion file. Each line of the file must contain the DN of * the target entry followed by one or more tab characters and the * attribute-value assertion in the form expected by the * {@link #parseAttributeValueAssertion} method. Empty lines and lines that * start with the octothorpe (#) character will be ignored. * * @param controls The controls to include in each of the compare requests. * It must not be {@code null} but may be empty. * * @return A list of the compare requests that should be issued. * * @throws LDAPException If a problem is encountered while parsing the * contents of the assertion file. */ @NotNull() private List readAssertionFile( @NotNull final Control[] controls) throws LDAPException { final File f = assertionFile.getValue(); try (FileReader fileReader = new FileReader(f); BufferedReader bufferedReader = new BufferedReader(fileReader)) { int lineNumber = 0; final List compareRequests = new ArrayList<>(); while (true) { // Read the next line from the file. If it is null, then we've hit the // end fo the file. If the line is empty or starts with an octothorpe, // then skip it and read the next line. final String line = bufferedReader.readLine(); if (line == null) { if (compareRequests.isEmpty()) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_ASSERTION_FILE_EMPTY.get(f.getAbsolutePath())); } return compareRequests; } lineNumber++; if (line.isEmpty() || line.startsWith("#")) { continue; } // Find the first tab on the line. Then, skip over any subsequent // tabs to find the assertion value. int tabPos = line.indexOf('\t'); if (tabPos < 0) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_LDAPCOMPARE_ASSERTION_FILE_LINE_MISSING_TAB.get( line, lineNumber, f.getAbsolutePath())); } final String dn = line.substring(0, tabPos); try { new DN(dn); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_LDAPCOMPARE_ASSERTION_FILE_LINE_INVALID_DN.get( line, lineNumber, f.getAbsolutePath(), dn, e.getMessage()), e); } for (int i=(tabPos+1); i < line.length(); i++) { if (line.charAt(i) == '\t') { tabPos = i; } } final String avaString = line.substring(tabPos+1); if (avaString.isEmpty()) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_LDAPCOMPARE_ASSERTION_FILE_LINE_MISSING_AVA.get( line, lineNumber, f.getAbsolutePath())); } final ObjectPair ava; try { ava = parseAttributeValueAssertion(avaString); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_LDAPCOMPARE_ASSERTION_FILE_CANNOT_PARSE_AVA.get( line, lineNumber, f.getAbsolutePath(), e.getMessage()), e); } compareRequests.add(new CompareRequest(dn, ava.getFirst(), ava.getSecond(), controls)); } } catch (final IOException e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_LDAPCOMPARE_CANNOT_READ_ASSERTION_FILE.get( f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } /** * Reads the DN file to obtain the DNs of the entries to target and creates * the list of compare requests to process. Each line of the file should * contain the DN of an entry to process. Empty lines and lines that start * with the octothorpe (#) character will be ignored. * * @param attributeName The name or OID of the attribute to target with * each of the compare requests. It must not be * {@code null} or empty. * @param assertionValue The assertion value to use for each of the * compare requests. It must not be {@code null}. * @param controls The controls to include in each of the compare * requests. It must not be {@code null} but may be * empty. * * @return A list of the compare requests that should be issued. * * @throws LDAPException If a problem is encountered while parsing the * contents of the assertion file. */ @NotNull() private List readDNFile(@NotNull final String attributeName, @NotNull final byte[] assertionValue, @NotNull final Control[] controls) throws LDAPException { try (DNFileReader dnFileReader = new DNFileReader(dnFile.getValue())) { final List compareRequests = new ArrayList<>(); while (true) { final DN dn; try { dn = dnFileReader.readDN(); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, e.getMessage(), e); } if (dn == null) { if (compareRequests.isEmpty()) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPCOMPARE_DN_FILE_EMPTY.get( dnFile.getValue().getAbsolutePath())); } return compareRequests; } compareRequests.add(new CompareRequest(dn, attributeName, assertionValue, controls)); } } catch (final IOException e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_LDAPCOMPARE_CANNOT_READ_DN_FILE.get( dnFile.getValue().getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } /** * Retrieves the controls that should be included in compare requests. * * @return The controls that should be included in compare requests, or an * empty array if no controls should be included. * * @throws LDAPException If a problem occurs while trying to create any of * the controls. */ @NotNull() private Control[] getCompareControls() throws LDAPException { final List controls = new ArrayList<>(); if (compareControl.isPresent()) { controls.addAll(compareControl.getValues()); } if (proxyAs.isPresent()) { controls.add(new ProxiedAuthorizationV2RequestControl( proxyAs.getValue())); } if (proxyV1As.isPresent()) { controls.add(new ProxiedAuthorizationV1RequestControl( proxyV1As.getValue())); } if (manageDsaIT.isPresent()) { controls.add(new ManageDsaITRequestControl(false)); } if (assertionFilter.isPresent()) { controls.add(new AssertionRequestControl(assertionFilter.getValue())); } if (operationPurpose.isPresent()) { controls.add(new OperationPurposeRequestControl(false, getToolName(), getToolVersion(), LDAPPasswordModify.class.getName() + ".getUpdateControls", operationPurpose.getValue())); } return controls.toArray(StaticUtils.NO_CONTROLS); } /** * Writes information about the compare result. * * @param writer The writer to use to write to the output file. It * may be {@code null} if no output file should be * used. * @param outputHandler The output handler that should be used to format the * result information. It must not be {@code null}. * @param request The compare request that was processed. It must not * be {@code null}. * @param result The result for the compare operation. It must not * be {@code null}. * * @throws LDAPException If a problem occurred while trying to write the * result. */ private void writeResult(@Nullable final PrintStream writer, @NotNull final LDAPCompareOutputHandler outputHandler, @NotNull final CompareRequest request, @NotNull final LDAPResult result) throws LDAPException { if (shouldWriteResultToStdErr(result)) { err(); err(INFO_LDAPCOMPARE_RESULT_HEADER.get()); err(INFO_LDAPCOMPARE_RESULT_HEADER_DN.get(request.getDN())); err(INFO_LDAPCOMPARE_RESULT_HEADER_ATTR.get(request.getAttributeName())); err(INFO_LDAPCOMPARE_RESULT_HEADER_VALUE.get( request.getAssertionValue())); for (final String line : ResultUtils.formatResult(result, true, 0, WRAP_COLUMN)) { err(line); } } final String message = outputHandler.formatResult(request, result); if (writer != null) { writer.println(message); } if ((writer == null) || teeOutput.isPresent()) { out(message); } } /** * Indicates whether to write information about the provided result to * standard error. * * @param result The result for which to make the determination. It must * not be {@code mull}. * * @return {@code true} if information about the result should be written to * standard error, or {@code false} if not. */ private boolean shouldWriteResultToStdErr(@NotNull final LDAPResult result) { if (verbose.isPresent()) { return true; } if (terse.isPresent()) { return false; } if (result.hasResponseControl()) { return true; } return ((result.getResultCode() != ResultCode.COMPARE_TRUE) && (result.getResultCode() != ResultCode.COMPARE_FALSE)); } /** * Writes the provided message and sets it as the completion message. * * @param isError Indicates whether the message should be written to * standard error rather than standard output. * @param message The message to be written. */ private void logCompletionMessage(final boolean isError, @NotNull final String message) { completionMessage.compareAndSet(null, message); if (! terse.isPresent()) { if (isError) { wrapErr(0, WRAP_COLUMN, message); } else { wrapOut(0, WRAP_COLUMN, message); } } } /** * {@inheritDoc} */ @Override() public void handleUnsolicitedNotification( @NotNull final LDAPConnection connection, @NotNull final ExtendedResult notification) { if (! terse.isPresent()) { final ArrayList lines = new ArrayList<>(10); ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, WRAP_COLUMN); for (final String line : lines) { err(line); } err(); } } /** * {@inheritDoc} */ @Override() @NotNull() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap<>(); examples.put( new String[] { "--hostname", "ds.example.com", "--port", "636", "--useSSL", "--bindDN", "uid=admin,dc=example,dc=com", "l:Austin", "uid=jdoe,ou=People,dc=example,dc=com" }, INFO_LDAPCOMPARE_EXAMPLE_1.get()); examples.put( new String[] { "--hostname", "ds.example.com", "--port", "636", "--useSSL", "--bindDN", "uid=admin,dc=example,dc=com", "--dnFile", "entry-dns.txt", "--outputFormat", "csv", "--terse", "title:manager" }, INFO_LDAPCOMPARE_EXAMPLE_2.get()); examples.put( new String[] { "--hostname", "ds.example.com", "--port", "636", "--useSSL", "--bindDN", "uid=admin,dc=example,dc=com", "--assertionFile", "compare-assertions.txt", "--outputFormat", "json", "--outputFile", "compare-assertion-results.json", "--verbose" }, INFO_LDAPCOMPARE_EXAMPLE_3.get()); return examples; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy