From 83a003e80f88643928410cf79df47a68c8a40fa9 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Apr 2023 15:06:37 +0200 Subject: [PATCH] Add list-profiles command --- .../main/java/sop/external/ExternalSOP.java | 7 +++ .../operation/GenerateKeyExternal.java | 6 +++ .../operation/ListProfilesExternal.java | 47 +++++++++++++++++++ .../operation/ExternalListProfilesTest.java | 13 +++++ .../src/main/java/sop/cli/picocli/SopCLI.java | 2 + .../cli/picocli/commands/GenerateKeyCmd.java | 6 +++ .../cli/picocli/commands/ListProfilesCmd.java | 39 +++++++++++++++ .../resources/msg_list-profiles.properties | 10 ++++ .../resources/msg_list-profiles_de.properties | 10 ++++ .../test/java/sop/cli/picocli/SOPTest.java | 6 +++ sop-java/src/main/java/sop/SOP.java | 2 + .../main/java/sop/operation/GenerateKey.java | 9 ++++ .../main/java/sop/operation/ListProfiles.java | 19 ++++++++ .../testsuite/operation/ListProfilesTest.java | 40 ++++++++++++++++ 14 files changed, 216 insertions(+) create mode 100644 external-sop/src/main/java/sop/external/operation/ListProfilesExternal.java create mode 100644 external-sop/src/test/java/sop/testsuite/external/operation/ExternalListProfilesTest.java create mode 100644 sop-java-picocli/src/main/java/sop/cli/picocli/commands/ListProfilesCmd.java create mode 100644 sop-java-picocli/src/main/resources/msg_list-profiles.properties create mode 100644 sop-java-picocli/src/main/resources/msg_list-profiles_de.properties create mode 100644 sop-java/src/main/java/sop/operation/ListProfiles.java create mode 100644 sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java diff --git a/external-sop/src/main/java/sop/external/ExternalSOP.java b/external-sop/src/main/java/sop/external/ExternalSOP.java index add37c5..f6e90a5 100644 --- a/external-sop/src/main/java/sop/external/ExternalSOP.java +++ b/external-sop/src/main/java/sop/external/ExternalSOP.java @@ -18,6 +18,7 @@ import sop.external.operation.GenerateKeyExternal; import sop.external.operation.InlineDetachExternal; import sop.external.operation.InlineSignExternal; import sop.external.operation.InlineVerifyExternal; +import sop.external.operation.ListProfilesExternal; import sop.external.operation.VersionExternal; import sop.operation.Armor; import sop.operation.Dearmor; @@ -30,6 +31,7 @@ import sop.operation.GenerateKey; import sop.operation.InlineDetach; import sop.operation.InlineSign; import sop.operation.InlineVerify; +import sop.operation.ListProfiles; import sop.operation.Version; import javax.annotation.Nonnull; @@ -154,6 +156,11 @@ public class ExternalSOP implements SOP { return new ArmorExternal(binaryName, properties); } + @Override + public ListProfiles listProfiles() { + return new ListProfilesExternal(binaryName, properties); + } + @Override public Dearmor dearmor() { return new DearmorExternal(binaryName, properties); diff --git a/external-sop/src/main/java/sop/external/operation/GenerateKeyExternal.java b/external-sop/src/main/java/sop/external/operation/GenerateKeyExternal.java index 95e86b8..e0ca97e 100644 --- a/external-sop/src/main/java/sop/external/operation/GenerateKeyExternal.java +++ b/external-sop/src/main/java/sop/external/operation/GenerateKeyExternal.java @@ -51,6 +51,12 @@ public class GenerateKeyExternal implements GenerateKey { return this; } + @Override + public GenerateKey profile(String profile) { + commandList.add("--profile=" + profile); + return this; + } + @Override public Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo { diff --git a/external-sop/src/main/java/sop/external/operation/ListProfilesExternal.java b/external-sop/src/main/java/sop/external/operation/ListProfilesExternal.java new file mode 100644 index 0000000..85eae6c --- /dev/null +++ b/external-sop/src/main/java/sop/external/operation/ListProfilesExternal.java @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.external.operation; + +import sop.external.ExternalSOP; +import sop.operation.ListProfiles; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +public class ListProfilesExternal extends ListProfiles { + + private final List commandList = new ArrayList<>(); + private final List envList; + + public ListProfilesExternal(String binary, Properties properties) { + this.commandList.add(binary); + this.commandList.add("list-profiles"); + this.envList = ExternalSOP.propertiesToEnv(properties); + } + + @Override + public List ofCommand(String command) { + commandList.add(command); + try { + String output = new String(ExternalSOP.executeProducingOperation(Runtime.getRuntime(), commandList, envList).getBytes()); + return Arrays.asList(output.split("\n")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public List global() { + try { + String output = new String(ExternalSOP.executeProducingOperation(Runtime.getRuntime(), commandList, envList).getBytes()); + return Arrays.asList(output.split("\n")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/external-sop/src/test/java/sop/testsuite/external/operation/ExternalListProfilesTest.java b/external-sop/src/test/java/sop/testsuite/external/operation/ExternalListProfilesTest.java new file mode 100644 index 0000000..18da883 --- /dev/null +++ b/external-sop/src/test/java/sop/testsuite/external/operation/ExternalListProfilesTest.java @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.testsuite.external.operation; + +import org.junit.jupiter.api.condition.EnabledIf; +import sop.testsuite.operation.ListProfilesTest; + +@EnabledIf("sop.testsuite.operation.AbstractSOPTest#hasBackends") +public class ExternalListProfilesTest extends ListProfilesTest { + +} diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java b/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java index 5f8b944..fab99e0 100644 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java +++ b/sop-java-picocli/src/main/java/sop/cli/picocli/SopCLI.java @@ -16,6 +16,7 @@ import sop.cli.picocli.commands.ExtractCertCmd; import sop.cli.picocli.commands.GenerateKeyCmd; import sop.cli.picocli.commands.InlineSignCmd; import sop.cli.picocli.commands.InlineVerifyCmd; +import sop.cli.picocli.commands.ListProfilesCmd; import sop.cli.picocli.commands.SignCmd; import sop.cli.picocli.commands.VerifyCmd; import sop.cli.picocli.commands.VersionCmd; @@ -41,6 +42,7 @@ import java.util.ResourceBundle; VerifyCmd.class, InlineSignCmd.class, InlineVerifyCmd.class, + ListProfilesCmd.class, VersionCmd.class, AutoComplete.GenerateCompletion.class } diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java index d215baf..a592863 100644 --- a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java +++ b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/GenerateKeyCmd.java @@ -30,6 +30,10 @@ public class GenerateKeyCmd extends AbstractSopCmd { paramLabel = "PASSWORD") String withKeyPassword; + @CommandLine.Option(names = "--profile", + paramLabel = "PROFILE") + String profile = "default"; + @Override public void run() { GenerateKey generateKey = throwIfUnsupportedSubcommand( @@ -43,6 +47,8 @@ public class GenerateKeyCmd extends AbstractSopCmd { generateKey.noArmor(); } + generateKey.profile(profile); + if (withKeyPassword != null) { try { String password = stringFromInputStream(getInput(withKeyPassword)); diff --git a/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ListProfilesCmd.java b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ListProfilesCmd.java new file mode 100644 index 0000000..14211c3 --- /dev/null +++ b/sop-java-picocli/src/main/java/sop/cli/picocli/commands/ListProfilesCmd.java @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.cli.picocli.commands; + +import picocli.CommandLine; +import sop.cli.picocli.SopCLI; +import sop.operation.ListProfiles; + +@CommandLine.Command(name = "list-profiles", + resourceBundle = "msg_list-profiles", + exitCodeOnInvalidInput = 37) +public class ListProfilesCmd extends AbstractSopCmd { + + @CommandLine.Parameters(paramLabel = "COMMAND", arity="0..1", descriptionKey = "subcommand") + String subcommand; + + @Override + public void run() { + ListProfiles listProfiles = throwIfUnsupportedSubcommand( + SopCLI.getSop().listProfiles(), "list-profiles"); + + if (subcommand == null) { + for (String profile : listProfiles.global()) { + // CHECKSTYLE:OFF + System.out.println(profile); + // CHECKSTYLE:ON + } + return; + } + + for (String profile : listProfiles.ofCommand(subcommand)) { + // CHECKSTYLE:OFF + System.out.println(profile); + // CHECKSTYLE:ON + } + } +} diff --git a/sop-java-picocli/src/main/resources/msg_list-profiles.properties b/sop-java-picocli/src/main/resources/msg_list-profiles.properties new file mode 100644 index 0000000..71bf5aa --- /dev/null +++ b/sop-java-picocli/src/main/resources/msg_list-profiles.properties @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Paul Schaub +# +# SPDX-License-Identifier: Apache-2.0 +usage.header=Emit a list of profiles supported by the identified subcommand +subcommand=Subcommand for which to list profiles +# Generic TODO: Remove when bumping picocli to 4.7.0 +usage.synopsisHeading=Usage:\u0020 +usage.commandListHeading = %nCommands:%n +usage.optionListHeading = %nOptions:%n +usage.footerHeading=Powered by picocli%n diff --git a/sop-java-picocli/src/main/resources/msg_list-profiles_de.properties b/sop-java-picocli/src/main/resources/msg_list-profiles_de.properties new file mode 100644 index 0000000..e6d8836 --- /dev/null +++ b/sop-java-picocli/src/main/resources/msg_list-profiles_de.properties @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Paul Schaub +# +# SPDX-License-Identifier: Apache-2.0 +usage.header=Gebe eine Liste von Profilen aus, welche vom angegebenen Unterbefehl unterstützt werden +subcommand=Unterbefehl für welchen Profile gelistet werden sollen +# Generic TODO: Remove when bumping picocli to 4.7.0 +usage.synopsisHeading=Aufruf:\u0020 +usage.commandListHeading=%nBefehle:%n +usage.optionListHeading = %nOptionen:%n +usage.footerHeading=Powered by Picocli%n diff --git a/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java b/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java index 9ca88da..c05440e 100644 --- a/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java +++ b/sop-java-picocli/src/test/java/sop/cli/picocli/SOPTest.java @@ -26,6 +26,7 @@ import sop.operation.InlineSign; import sop.operation.InlineVerify; import sop.operation.DetachedSign; import sop.operation.DetachedVerify; +import sop.operation.ListProfiles; import sop.operation.Version; public class SOPTest { @@ -95,6 +96,11 @@ public class SOPTest { return null; } + @Override + public ListProfiles listProfiles() { + return null; + } + @Override public InlineDetach inlineDetach() { return null; diff --git a/sop-java/src/main/java/sop/SOP.java b/sop-java/src/main/java/sop/SOP.java index 284a0ab..7ce617a 100644 --- a/sop-java/src/main/java/sop/SOP.java +++ b/sop-java/src/main/java/sop/SOP.java @@ -15,6 +15,7 @@ import sop.operation.InlineSign; import sop.operation.InlineVerify; import sop.operation.DetachedSign; import sop.operation.DetachedVerify; +import sop.operation.ListProfiles; import sop.operation.Version; /** @@ -146,4 +147,5 @@ public interface SOP { */ Dearmor dearmor(); + ListProfiles listProfiles(); } diff --git a/sop-java/src/main/java/sop/operation/GenerateKey.java b/sop-java/src/main/java/sop/operation/GenerateKey.java index af7275b..1eb2620 100644 --- a/sop-java/src/main/java/sop/operation/GenerateKey.java +++ b/sop-java/src/main/java/sop/operation/GenerateKey.java @@ -6,6 +6,7 @@ package sop.operation; import java.io.IOException; import java.io.InputStream; +import java.util.List; import sop.Ready; import sop.exception.SOPGPException; @@ -56,6 +57,14 @@ public interface GenerateKey { return withKeyPassword(UTF8Util.decodeUTF8(password)); } + /** + * Pass in a profile identifier. + * + * @param profile profile identifier + * @return this + */ + GenerateKey profile(String profile); + /** * Generate the OpenPGP key and return it encoded as an {@link InputStream}. * diff --git a/sop-java/src/main/java/sop/operation/ListProfiles.java b/sop-java/src/main/java/sop/operation/ListProfiles.java new file mode 100644 index 0000000..bdfa811 --- /dev/null +++ b/sop-java/src/main/java/sop/operation/ListProfiles.java @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.operation; + +import java.util.List; + +public abstract class ListProfiles { + + public ListProfiles() { + + } + + public abstract List ofCommand(String command); + + public abstract List global(); + +} diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java b/sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java new file mode 100644 index 0000000..20f0590 --- /dev/null +++ b/sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.testsuite.operation; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import sop.SOP; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class ListProfilesTest extends AbstractSOPTest { + + static Stream provideInstances() { + return provideBackends(); + } + + @ParameterizedTest + @MethodSource("provideInstances") + public void listGlobalProfiles(SOP sop) throws IOException { + List profiles = sop.listProfiles() + .global(); + assertFalse(profiles.isEmpty()); + } + + @ParameterizedTest + @MethodSource("provideInstances") + public void listGenerateKeyProfiles(SOP sop) throws IOException { + List profiles = sop.listProfiles() + .ofCommand("generate-key"); + assertFalse(profiles.isEmpty()); + } + +}