/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2026 3A Systems, LLC. */ package com.forgerock.opendj.ldap.tools; import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_TOOL_INVALID_CONTROL_STRING; import static com.forgerock.opendj.util.OperatingSystem.isWindows; import static org.fest.assertions.Assertions.assertThat; import static org.testng.Assert.fail; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.controls.Control; import org.forgerock.testng.ForgeRockTestCase; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.forgerock.opendj.cli.CommonArguments; import com.forgerock.opendj.cli.StringArgument; /** Tests the parsing of the {@code -J/--control} argument by {@link Utils#readControls(StringArgument)}. */ @Test public final class ControlArgumentTestCase extends ForgeRockTestCase { @DataProvider public Object[][] validControls() { return new Object[][] { { "1.2.3.4", "1.2.3.4", false, null }, { "1.2.3.4:true", "1.2.3.4", true, null }, { "1.2.3.4:FALSE:value", "1.2.3.4", false, "value" }, { "1.2.3.4:true:", "1.2.3.4", true, "" }, { "pwpolicy:true", "1.3.6.1.4.1.42.2.27.8.5.1", true, null }, // Everything after the second colon is the value, colons included. { "2.16.840.1.113730.3.4.18:true:dn:uid=bjensen,ou=People,dc=example,dc=com", "2.16.840.1.113730.3.4.18", true, "dn:uid=bjensen,ou=People,dc=example,dc=com" }, { "2.16.840.1.113730.3.4.18:true:u:bjensen", "2.16.840.1.113730.3.4.18", true, "u:bjensen" }, { "1.2.3.4:false:urn:example:value", "1.2.3.4", false, "urn:example:value" }, { "1.2.3.4:false:ldap://host:1389/dc=example", "1.2.3.4", false, "ldap://host:1389/dc=example" }, // The base64 form decodes to a value that can itself hold colons. { "1.2.3.4:true::" + base64("dn:uid=bjensen"), "1.2.3.4", true, "dn:uid=bjensen" }, { "1.2.3.4:true::", "1.2.3.4", true, "" }, }; } @Test(dataProvider = "validControls") public void testValidControl(final String argument, final String oid, final boolean critical, final String value) throws Exception { final Control control = readSingleControl(argument); assertThat(control.getOID()).isEqualTo(oid); assertThat(control.isCritical()).isEqualTo(critical); if (value == null) { assertThat(control.hasValue()).isFalse(); } else { assertThat(control.hasValue()).isTrue(); assertThat(control.getValue().toString()).isEqualTo(value); } } @Test public void testValueReadFromFileWhosePathHoldsAColon() throws Exception { // On Windows the drive letter puts a colon in every absolute path; elsewhere the file name carries one. final File dir = Files.createTempDirectory("control-value").toFile(); final File file = new File(dir, isWindows() ? "control-value.ber" : "control:value.ber"); try { final byte[] bytes = { 0x04, 0x03, 'a', ':', 'b' }; Files.write(file.toPath(), bytes); final Control control = readSingleControl("1.2.3.4:true:<" + file.getAbsolutePath()); assertThat(control.getOID()).isEqualTo("1.2.3.4"); assertThat(control.isCritical()).isTrue(); assertThat(control.getValue()).isEqualTo(ByteString.wrap(bytes)); } finally { file.delete(); dir.delete(); } } @DataProvider public Object[][] invalidControls() { final String missingFile = new File(System.getProperty("java.io.tmpdir"), "no-such-dir-1156/value.ber") .getAbsolutePath(); return new Object[][] { { "1.2.3.4:invalidcriticality" }, { "1.2.3.4:invalidcriticality:value" }, { "1.2.3.4:true:<" + missingFile }, { "1.2.3.4:true::not*base64" }, }; } @Test(dataProvider = "invalidControls") public void testInvalidControlIsReportedAsAnInvalidControlString(final String argument) throws Exception { try { readControls(argument); fail("Expected the control '" + argument + "' to be rejected"); } catch (final LDAPToolException e) { assertThat(e.getResultCode()).isEqualTo(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue()); assertThat(e.getMessage()).isEqualTo(ERR_TOOL_INVALID_CONTROL_STRING.get(argument).toString()); } } private static Control readSingleControl(final String argument) throws Exception { final List controls = readControls(argument); assertThat(controls).hasSize(1); return controls.get(0); } private static List readControls(final String argument) throws Exception { final StringArgument controlArg = CommonArguments.controlArgument(); controlArg.addValue(argument); controlArg.setPresent(true); return Utils.readControls(controlArg); } private static String base64(final String value) { return ByteString.valueOfBytes(value.getBytes(StandardCharsets.UTF_8)).toBase64String(); } }