diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index fab075a..2138cb4 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -16,4 +16,6 @@ steps: - gradle check javadocAll # Code has coverage - gradle jacocoRootReport coveralls - secrets: [coveralls_repo_token] + environment: + coveralls_repo_token: + from_secret: coveralls_repo_token diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c266f1..3028216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,37 @@ SPDX-License-Identifier: Apache-2.0 # Changelog -## 10.0.1-SNAPSHOT +## 10.1.1-SNAPSHOT +- Prepare jar files for use in native images, e.g. using GraalVM by generating and including + configuration files for reflection, resources and dynamic proxies. + +## 10.1.0 +- `sop-java`: + - Remove `label()` option from `armor()` subcommand + - Move test-fixtures artifact built with the `testFixtures` plugin into + its own module `sop-java-testfixtures`, which can be consumed by maven builds. +- `sop-java-picocli`: + - Properly map `MissingParameterException` to `MissingArg` exit code + - As a workaround for native builds using graalvm: + - Do not re-set message bundles dynamically (fails in native builds) + - Prevent an unmatched argument error + +## 10.0.3 +- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report) +- Backport: `revoke-key`: Allow for multiple password options + +## 10.0.2 +- Downgrade `logback-core` to `1.2.13` + +## 10.0.1 - Remove `label()` option from `Armor` operation - Fix exit code for 'Missing required option/parameter' error - Fix `revoke-key`: Allow for multiple invocations of `--with-key-password` option +- Fix `EncryptExternal` use of `--sign-with` parameter +- Fix `NullPointerException` in `DecryptExternal` when reading lines +- Fix `DecryptExternal` use of `verifications-out` +- Test suite: Ignore tests if `UnsupportedOption` is thrown +- Bump `logback-core` to `1.4.14` ## 10.0.0 - Update implementation to [SOP Specification revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html). @@ -17,6 +44,10 @@ SPDX-License-Identifier: Apache-2.0 - Introduce `sopv` interface subset with revision `1.0` - Add `sop version --sopv` +## 8.0.2 +- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report) +- Backport: `revoke-key`: Allow for multiple password options + ## 8.0.1 - `decrypt`: Do not throw `NoSignature` exception (exit code 3) if `--verify-with` is provided, but `VERIFICATIONS` is empty. @@ -35,6 +66,13 @@ SPDX-License-Identifier: Apache-2.0 - Change `EncryptAs` values into lowercase - Change `SignAs` values into lowercase +## 7.0.2 +- CLI `change-key-password`: Fix indirect parameter passing for new and old passwords (thanks to @dkg for the report) +- Backport: revoke-key command: Allow for multiple '--with-key-password' options + +## 7.0.1 +- `decrypt`: Do not throw `NoSignature` exception (exit code 3) if `--verify-with` is provided, but `VERIFICATIONS` is empty. + ## 7.0.0 - Update implementation to [SOP Specification revision 07](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-07.html). - Add support for new `revoke-key` subcommand diff --git a/README.md b/README.md index baeb874..f2e207f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ The repository contains the following modules: compatible with the SOP-CLI specification. * [external-sop](/external-sop) contains an API implementation that can be used to forward API calls to a SOP executable, allowing to delegate the implementation logic to an arbitrary SOP CLI implementation. +* [sop-java-testfixtures](/sop-java-testfixtures) contains a test suite that can be shared by downstream implementations + of `sop-java`. ## Known Implementations (Please expand!) @@ -35,9 +37,11 @@ allowing to delegate the implementation logic to an arbitrary SOP CLI implementa |-------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | [pgpainless-sop](https://github.com/pgpainless/pgpainless/tree/main/pgpainless-sop) | Implementation of `sop-java` using PGPainless | | [external-sop](https://github.com/pgpainless/sop-java/tree/main/external-sop) | Implementation of `sop-java` that allows binding to external SOP binaries such as `sqop` | +| [bcsop](https://codeberg.org/PGPainless/bc-sop) | Implementation of `sop-java` using vanilla Bouncy Castle | ### Implementations in other languages -| Project | Language | -|-------------------------------------------------|----------| -| [sop-rs](https://sequoia-pgp.gitlab.io/sop-rs/) | Rust | -| [SOP for python](https://pypi.org/project/sop/) | Python | +| Project | Language | +|---------------------------------------------------|----------| +| [sop-rs](https://sequoia-pgp.gitlab.io/sop-rs/) | Rust | +| [SOP for python](https://pypi.org/project/sop/) | Python | +| [rpgpie-sop](https://crates.io/crates/rpgpie-sop) | Rust | diff --git a/build.gradle b/build.gradle index 577c2aa..78a7267 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { plugins { id 'ru.vyarus.animalsniffer' version '1.5.3' - id 'org.jetbrains.kotlin.jvm' version "1.8.10" + id 'org.jetbrains.kotlin.jvm' version "1.9.21" id 'com.diffplug.spotless' version '6.22.0' apply false } @@ -32,6 +32,7 @@ allprojects { apply plugin: 'jacoco' apply plugin: 'checkstyle' apply plugin: 'kotlin' + apply plugin: 'kotlin-kapt' apply plugin: 'com.diffplug.spotless' // For non-cli modules enable android api compatibility check diff --git a/external-sop/build.gradle b/external-sop/build.gradle index 1bb86fc..d1a7ffb 100644 --- a/external-sop/build.gradle +++ b/external-sop/build.gradle @@ -27,7 +27,7 @@ dependencies { // The ExternalTestSubjectFactory reads json config file to find configured SOP binaries... testImplementation "com.google.code.gson:gson:$gsonVersion" // ...and extends TestSubjectFactory - testImplementation(testFixtures(project(":sop-java"))) + testImplementation(project(":sop-java-testfixtures")) } test { diff --git a/external-sop/src/main/kotlin/sop/external/operation/DecryptExternal.kt b/external-sop/src/main/kotlin/sop/external/operation/DecryptExternal.kt index e0a900d..1e6d6a2 100644 --- a/external-sop/src/main/kotlin/sop/external/operation/DecryptExternal.kt +++ b/external-sop/src/main/kotlin/sop/external/operation/DecryptExternal.kt @@ -78,7 +78,7 @@ class DecryptExternal( val verifyOut = File(tempDir, "verifications-out") verifyOut.delete() if (requireVerification) { - commandList.add("--verify-out=${verifyOut.absolutePath}") + commandList.add("--verifications-out=${verifyOut.absolutePath}") } try { diff --git a/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt b/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt index 12d9cff..679e09b 100644 --- a/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt +++ b/external-sop/src/main/kotlin/sop/external/operation/EncryptExternal.kt @@ -37,7 +37,7 @@ class EncryptExternal( override fun signWith(key: InputStream): Encrypt = apply { commandList.add("--sign-with=@ENV:SIGN_WITH_$argCounter") - envList.add("SIGN_WITH_$argCounter=${ExternalSOP.readString(key)}") + envList.add("SIGN_WITH_$argCounter=${readString(key)}") argCounter += 1 } diff --git a/settings.gradle b/settings.gradle index 5dc6372..1cb66be 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,5 +6,6 @@ rootProject.name = 'SOP-Java' include 'sop-java', 'sop-java-picocli', + 'sop-java-testfixtures', 'external-sop' diff --git a/sop-java-picocli/build.gradle b/sop-java-picocli/build.gradle index 0596ad3..2203abe 100644 --- a/sop-java-picocli/build.gradle +++ b/sop-java-picocli/build.gradle @@ -17,11 +17,11 @@ dependencies { // SOP implementation(project(":sop-java")) - testImplementation(testFixtures(project(":sop-java"))) + testImplementation(project(":sop-java-testfixtures")) // CLI implementation "info.picocli:picocli:$picocliVersion" - annotationProcessor "info.picocli:picocli-codegen:$picocliVersion" + kapt "info.picocli:picocli-codegen:$picocliVersion" // @Nonnull, @Nullable... implementation "com.google.code.findbugs:jsr305:$jsrVersion" @@ -33,6 +33,10 @@ application { mainClass = mainClassName } +compileJava { + options.compilerArgs += ["-Aproject=${project.group}/${project.name}"] +} + jar { dependsOn(":sop-java:jar") duplicatesStrategy(DuplicatesStrategy.EXCLUDE) diff --git a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt index 6caf2ff..943f0f3 100644 --- a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt +++ b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/SopCLI.kt @@ -64,7 +64,7 @@ class SopCLI { @JvmField var EXECUTABLE_NAME = "sop" @JvmField - @Option(names = ["--stacktrace"], scope = CommandLine.ScopeType.INHERIT) + @Option(names = ["--stacktrace"], scope = ScopeType.INHERIT) var stacktrace = false @JvmStatic @@ -78,33 +78,25 @@ class SopCLI { @JvmStatic fun execute(vararg args: String): Int { // Set locale - CommandLine(InitLocale()).parseArgs(*args) + CommandLine(InitLocale()).setUnmatchedArgumentsAllowed(true).parseArgs(*args) // Re-set bundle with updated locale cliMsg = ResourceBundle.getBundle("msg_sop") - val cmd = - CommandLine(SopCLI::class.java).apply { - // explicitly set help command resource bundle - subcommands["help"]?.setResourceBundle(ResourceBundle.getBundle("msg_help")) + return CommandLine(SopCLI::class.java) + .apply { // Hide generate-completion command subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true) + // render Input/Output sections in help command + subcommands.values.filter { (it.getCommand() as Any) is AbstractSopCmd } // Only for AbstractSopCmd objects + .forEach { (it.getCommand() as AbstractSopCmd).installIORenderer(it) } // overwrite executable name commandName = EXECUTABLE_NAME // setup exception handling executionExceptionHandler = SOPExecutionExceptionHandler() exitCodeExceptionMapper = SOPExceptionExitCodeMapper() isCaseInsensitiveEnumValuesAllowed = true - } - - // render Input/Output sections in help command - cmd.subcommands.values - .filter { - (it.getCommand() as Any) is AbstractSopCmd - } // Only for AbstractSopCmd objects - .forEach { (it.getCommand() as AbstractSopCmd).installIORenderer(it) } - - return cmd.execute(*args) + }.execute(*args) } } diff --git a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/ChangeKeyPasswordCmd.kt b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/ChangeKeyPasswordCmd.kt index 0c2eb4a..be37309 100644 --- a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/ChangeKeyPasswordCmd.kt +++ b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/ChangeKeyPasswordCmd.kt @@ -33,9 +33,15 @@ class ChangeKeyPasswordCmd : AbstractSopCmd() { changeKeyPassword.noArmor() } - oldKeyPasswords.forEach { changeKeyPassword.oldKeyPassphrase(it) } + oldKeyPasswords.forEach { + val password = stringFromInputStream(getInput(it)) + changeKeyPassword.oldKeyPassphrase(password) + } - newKeyPassword?.let { changeKeyPassword.newKeyPassphrase(it) } + newKeyPassword?.let { + val password = stringFromInputStream(getInput(it)) + changeKeyPassword.newKeyPassphrase(password) + } try { changeKeyPassword.keys(System.`in`).writeTo(System.out) diff --git a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/DecryptCmd.kt b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/DecryptCmd.kt index 3f15f07..de98f17 100644 --- a/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/DecryptCmd.kt +++ b/sop-java-picocli/src/main/kotlin/sop/cli/picocli/commands/DecryptCmd.kt @@ -29,7 +29,7 @@ class DecryptCmd : AbstractSopCmd() { @Option(names = [OPT_WITH_PASSWORD], paramLabel = "PASSWORD") var withPassword: List = listOf() - @Option(names = [OPT_VERIFICATIONS_OUT], paramLabel = "VERIFICATIONS") + @Option(names = [OPT_VERIFICATIONS_OUT, "--verify-out"], paramLabel = "VERIFICATIONS") var verifyOut: String? = null @Option(names = [OPT_VERIFY_WITH], paramLabel = "CERT") var certs: List = listOf() diff --git a/sop-java-testfixtures/build.gradle b/sop-java-testfixtures/build.gradle new file mode 100644 index 0000000..d3d4a1e --- /dev/null +++ b/sop-java-testfixtures/build.gradle @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +plugins { + id 'java-library' +} + +group 'org.pgpainless' + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":sop-java")) + implementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + implementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" + runtimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + // @Nullable, @Nonnull annotations + implementation "com.google.code.findbugs:jsr305:3.0.2" + +} diff --git a/sop-java/src/testFixtures/java/sop/testsuite/JUtils.java b/sop-java-testfixtures/src/main/java/sop/testsuite/JUtils.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/JUtils.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/JUtils.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/SOPInstanceFactory.java b/sop-java-testfixtures/src/main/java/sop/testsuite/SOPInstanceFactory.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/SOPInstanceFactory.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/SOPInstanceFactory.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/TestData.java b/sop-java-testfixtures/src/main/java/sop/testsuite/TestData.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/TestData.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/TestData.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/assertions/SopExecutionAssertions.java b/sop-java-testfixtures/src/main/java/sop/testsuite/assertions/SopExecutionAssertions.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/assertions/SopExecutionAssertions.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/assertions/SopExecutionAssertions.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/assertions/VerificationAssert.java b/sop-java-testfixtures/src/main/java/sop/testsuite/assertions/VerificationAssert.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/assertions/VerificationAssert.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/assertions/VerificationAssert.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/assertions/VerificationListAssert.java b/sop-java-testfixtures/src/main/java/sop/testsuite/assertions/VerificationListAssert.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/assertions/VerificationListAssert.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/assertions/VerificationListAssert.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/assertions/package-info.java b/sop-java-testfixtures/src/main/java/sop/testsuite/assertions/package-info.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/assertions/package-info.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/assertions/package-info.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/AbstractSOPTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/AbstractSOPTest.java similarity index 88% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/AbstractSOPTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/AbstractSOPTest.java index 8595898..6c163f7 100644 --- a/sop-java/src/testFixtures/java/sop/testsuite/operation/AbstractSOPTest.java +++ b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/AbstractSOPTest.java @@ -5,8 +5,11 @@ package sop.testsuite.operation; import org.junit.jupiter.api.Named; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.provider.Arguments; import sop.SOP; +import sop.testsuite.AbortOnUnsupportedOption; +import sop.testsuite.AbortOnUnsupportedOptionExtension; import sop.testsuite.SOPInstanceFactory; import java.lang.reflect.InvocationTargetException; @@ -15,6 +18,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +@ExtendWith(AbortOnUnsupportedOptionExtension.class) +@AbortOnUnsupportedOption public abstract class AbstractSOPTest { private static final List backends = new ArrayList<>(); diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/ArmorDearmorTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/ArmorDearmorTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/ArmorDearmorTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/ArmorDearmorTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/ChangeKeyPasswordTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/ChangeKeyPasswordTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/ChangeKeyPasswordTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/ChangeKeyPasswordTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/DecryptWithSessionKeyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/DecryptWithSessionKeyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/DecryptWithSessionKeyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/DecryptWithSessionKeyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/DetachedSignDetachedVerifyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/DetachedSignDetachedVerifyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/DetachedSignDetachedVerifyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/DetachedSignDetachedVerifyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/EncryptDecryptTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/EncryptDecryptTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/EncryptDecryptTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/EncryptDecryptTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/ExtractCertTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/ExtractCertTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/ExtractCertTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/ExtractCertTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/GenerateKeyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/GenerateKeyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/GenerateKeyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/GenerateKeyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/InlineSignInlineDetachDetachedVerifyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/InlineSignInlineDetachDetachedVerifyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/InlineSignInlineDetachDetachedVerifyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/InlineSignInlineDetachDetachedVerifyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/InlineSignInlineVerifyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/InlineSignInlineVerifyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/InlineSignInlineVerifyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/InlineSignInlineVerifyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/ListProfilesTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/ListProfilesTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/ListProfilesTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/RevokeKeyTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/RevokeKeyTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/RevokeKeyTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/RevokeKeyTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/VersionTest.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/VersionTest.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/VersionTest.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/operation/package-info.java b/sop-java-testfixtures/src/main/java/sop/testsuite/operation/package-info.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/operation/package-info.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/operation/package-info.java diff --git a/sop-java/src/testFixtures/java/sop/testsuite/package-info.java b/sop-java-testfixtures/src/main/java/sop/testsuite/package-info.java similarity index 100% rename from sop-java/src/testFixtures/java/sop/testsuite/package-info.java rename to sop-java-testfixtures/src/main/java/sop/testsuite/package-info.java diff --git a/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOption.kt b/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOption.kt new file mode 100644 index 0000000..cf99671 --- /dev/null +++ b/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOption.kt @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.testsuite + +import java.lang.annotation.Inherited + +@Target(AnnotationTarget.TYPE) +@Retention(AnnotationRetention.RUNTIME) +@Inherited +annotation class AbortOnUnsupportedOption diff --git a/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOptionExtension.kt b/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOptionExtension.kt new file mode 100644 index 0000000..809c78f --- /dev/null +++ b/sop-java-testfixtures/src/main/kotlin/sop/testsuite/AbortOnUnsupportedOptionExtension.kt @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop.testsuite + +import org.junit.jupiter.api.Assumptions +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler +import sop.exception.SOPGPException + +class AbortOnUnsupportedOptionExtension : TestExecutionExceptionHandler { + override fun handleTestExecutionException(context: ExtensionContext, throwable: Throwable) { + val testClass = context.requiredTestClass + val annotation = testClass.getAnnotation(AbortOnUnsupportedOption::class.java) + if (annotation != null && SOPGPException.UnsupportedOption::class.isInstance(throwable)) { + Assumptions.assumeTrue(false, "Test aborted due to: " + throwable.message) + } + throw throwable + } +} diff --git a/sop-java/build.gradle b/sop-java/build.gradle index ca546bf..c6f4e4e 100644 --- a/sop-java/build.gradle +++ b/sop-java/build.gradle @@ -1,10 +1,12 @@ +import org.apache.tools.ant.filters.ReplaceTokens + // SPDX-FileCopyrightText: 2021 Paul Schaub // // SPDX-License-Identifier: Apache-2.0 +import org.apache.tools.ant.filters.* plugins { id 'java-library' - id 'java-test-fixtures' } group 'org.pgpainless' @@ -17,14 +19,19 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" - testFixturesImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" - testFixturesImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" + testImplementation(project(":sop-java-testfixtures")) // @Nullable, @Nonnull annotations implementation "com.google.code.findbugs:jsr305:3.0.2" } +processResources { + filter ReplaceTokens, tokens: [ + "project.version": project.version.toString() + ] +} + test { useJUnitPlatform() } diff --git a/sop-java/src/main/kotlin/sop/SigningResult.kt b/sop-java/src/main/kotlin/sop/SigningResult.kt index 29304ea..60888e0 100644 --- a/sop-java/src/main/kotlin/sop/SigningResult.kt +++ b/sop-java/src/main/kotlin/sop/SigningResult.kt @@ -9,8 +9,9 @@ package sop * * @param micAlg string identifying the digest mechanism used to create the signed message. This is * useful for setting the `micalg=` parameter for the multipart/signed content-type of a PGP/MIME - * object as described in section 5 of [RFC3156]. If more than one signature was generated and - * different digest mechanisms were used, the value of the micalg object is an empty string. + * object as described in section 5 of [RFC3156](https://www.rfc-editor.org/rfc/rfc3156#section-5). + * If more than one signature was generated and different digest mechanisms were used, the value + * of the micalg object is an empty string. */ data class SigningResult(val micAlg: MicAlg) { diff --git a/sop-java/src/main/kotlin/sop/operation/GenerateKey.kt b/sop-java/src/main/kotlin/sop/operation/GenerateKey.kt index 3b83b99..13de39a 100644 --- a/sop-java/src/main/kotlin/sop/operation/GenerateKey.kt +++ b/sop-java/src/main/kotlin/sop/operation/GenerateKey.kt @@ -78,7 +78,7 @@ interface GenerateKey { fun signingOnly(): GenerateKey /** - * Generate the OpenPGP key and return it encoded as an [InputStream]. + * Generate the OpenPGP key and return it encoded as an [java.io.InputStream]. * * @return key * @throws MissingArg if no user-id was provided diff --git a/sop-java/src/main/kotlin/sop/operation/Version.kt b/sop-java/src/main/kotlin/sop/operation/Version.kt index 5f26491..a10fe7c 100644 --- a/sop-java/src/main/kotlin/sop/operation/Version.kt +++ b/sop-java/src/main/kotlin/sop/operation/Version.kt @@ -4,6 +4,9 @@ package sop.operation +import java.io.IOException +import java.io.InputStream +import java.util.* import kotlin.jvm.Throws import sop.exception.SOPGPException @@ -107,4 +110,17 @@ interface Version { * this method throws an [SOPGPException.UnsupportedOption] instead. */ @Throws(SOPGPException.UnsupportedOption::class) fun getSopVVersion(): String + + /** Return the current version of the SOP-Java library. */ + fun getSopJavaVersion(): String? { + return try { + val resourceIn: InputStream = + javaClass.getResourceAsStream("/sop-java-version.properties") + ?: throw IOException("File sop-java-version.properties not found.") + val properties = Properties().apply { load(resourceIn) } + properties.getProperty("sop-java-version") + } catch (e: IOException) { + null + } + } } diff --git a/sop-java/src/main/kotlin/sop/util/ProxyOutputStream.kt b/sop-java/src/main/kotlin/sop/util/ProxyOutputStream.kt index a608c89..4ba24b8 100644 --- a/sop-java/src/main/kotlin/sop/util/ProxyOutputStream.kt +++ b/sop-java/src/main/kotlin/sop/util/ProxyOutputStream.kt @@ -16,6 +16,7 @@ import java.io.OutputStream * target output stream is not yet known. */ @Deprecated("Marked for removal.") +// TODO: Remove in 11.X class ProxyOutputStream : OutputStream() { private val buffer = ByteArrayOutputStream() private var swapped: OutputStream? = null diff --git a/sop-java/src/main/resources/sop-java-version.properties b/sop-java/src/main/resources/sop-java-version.properties new file mode 100644 index 0000000..a2f509b --- /dev/null +++ b/sop-java/src/main/resources/sop-java-version.properties @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2025 Paul Schaub +# +# SPDX-License-Identifier: Apache-2.0 +sop-java-version=@project.version@ \ No newline at end of file diff --git a/version.gradle b/version.gradle index 7a18825..f98a318 100644 --- a/version.gradle +++ b/version.gradle @@ -12,7 +12,7 @@ allprojects { jsrVersion = '3.0.2' junitVersion = '5.8.2' junitSysExitVersion = '1.1.2' - logbackVersion = '1.2.13' + logbackVersion = '1.2.13' // 1.4+ cause CLI spam mockitoVersion = '4.5.1' picocliVersion = '4.6.3' slf4jVersion = '1.7.36'