From c9f988b2d18caf7e0609fa73813e986ac3b25674 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 26 Sep 2023 17:38:07 +0200 Subject: [PATCH] Kotlin conversion: SelectUserId --- .../util/selection/userid/SelectUserId.java | 231 ------------------ .../util/selection/userid/package-info.java | 8 - .../secretkeyring/SecretKeyRingEditor.kt | 37 ++- .../SecretKeyRingEditorInterface.kt | 78 +++++- .../util/selection/userid/SelectUserId.kt | 104 ++++++++ .../selection/userid/SelectUserIdTest.java | 78 +++--- 6 files changed, 240 insertions(+), 296 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/SelectUserId.java delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/package-info.java create mode 100644 pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/SelectUserId.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/SelectUserId.java deleted file mode 100644 index 5c1611af..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/SelectUserId.java +++ /dev/null @@ -1,231 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.util.selection.userid; - -import java.util.ArrayList; -import java.util.List; - -import org.bouncycastle.openpgp.PGPKeyRing; -import org.pgpainless.PGPainless; -import org.pgpainless.key.info.KeyRingInfo; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * Filter for selecting user-ids from keys and from lists. - */ -public abstract class SelectUserId { - - /** - * Return true, if the given user-id is accepted by this particular filter, false otherwise. - * - * @param userId user-id - * @return acceptance of the filter - */ - protected abstract boolean accept(String userId); - - /** - * Select all currently valid user-ids of the given key ring. - * - * @param keyRing public or secret key ring - * @return valid user-ids - */ - @Nonnull - public List selectUserIds(@Nonnull PGPKeyRing keyRing) { - List userIds = PGPainless.inspectKeyRing(keyRing).getValidUserIds(); - return selectUserIds(userIds); - } - - /** - * Select all acceptable (see {@link #accept(String)}) from the given list of user-ids. - * - * @param userIds list of user-ids - * @return sub-list of acceptable user-ids - */ - @Nonnull - public List selectUserIds(@Nonnull List userIds) { - List selected = new ArrayList<>(); - for (String userId : userIds) { - if (accept(userId)) { - selected.add(userId); - } - } - return selected; - } - - /** - * Return the first valid, acceptable user-id from the given public or secret key ring. - * - * @param keyRing public or secret key ring - * @return first matching valid user-id or null - */ - @Nullable - public String firstMatch(PGPKeyRing keyRing) { - return firstMatch(selectUserIds(keyRing)); - } - - /** - * Return the first valid, acceptable user-id from the list of user-ids. - * - * @param userIds list of user-ids - * @return first matching valid user-id or null - */ - @Nullable - public String firstMatch(@Nonnull List userIds) { - for (String userId : userIds) { - if (accept(userId)) { - return userId; - } - } - return null; - } - - /** - * Filter that filters for user-ids which contain the given
query
as a substring. - * - * @param query query - * @return filter - */ - public static SelectUserId containsSubstring(@Nonnull CharSequence query) { - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - return userId.contains(query.toString()); - } - }; - } - - /** - * Filter that filters for user-ids which match the given
query
exactly. - * - * @param query query - * @return filter - */ - public static SelectUserId exactMatch(@Nonnull CharSequence query) { - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - return userId.equals(query.toString()); - } - }; - } - - /** - * Filter that filters for user-ids which start with the given
substring
. - * - * @param substring substring - * @return filter - */ - public static SelectUserId startsWith(@Nonnull CharSequence substring) { - String string = substring.toString(); - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - return userId.startsWith(string); - } - }; - } - - /** - * Filter that filters for user-ids which contain the given
email
address. - * Note: This only accepts user-ids which properly have the email address surrounded by angle brackets. - * - * The argument
email
can both be a plain email address (
"foo@bar.baz"
), - * or surrounded by angle brackets ({@code
""
}), the result of the filter will be the same. - * - * @param email email address - * @return filter - */ - public static SelectUserId containsEmailAddress(@Nonnull CharSequence email) { - String string = email.toString(); - return containsSubstring(string.matches("^<.+>$") ? string : '<' + string + '>'); - } - - /** - * Filter that filters for valid user-ids on the given
keyRing
only. - * - * @param keyRing public / secret keys - * @return filter - */ - public static SelectUserId validUserId(PGPKeyRing keyRing) { - final KeyRingInfo info = PGPainless.inspectKeyRing(keyRing); - - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - return info.isUserIdValid(userId); - } - }; - } - - /** - * Filter that filters for user-ids which pass all the given
filters
. - * - * @param filters filters - * @return filter - */ - public static SelectUserId and(SelectUserId... filters) { - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - boolean accept = true; - for (SelectUserId filter : filters) { - accept &= filter.accept(userId); - } - return accept; - } - }; - } - - /** - * Filter that filters for user-ids which pass at least one of the given
filters
. - * - * @param filters filters - * @return filter - */ - public static SelectUserId or(SelectUserId... filters) { - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - boolean accept = false; - for (SelectUserId filter : filters) { - accept |= filter.accept(userId); - } - return accept; - } - }; - } - - /** - * Filter that inverts the result of the given
filter
. - * - * @param filter filter - * @return inverting filter - */ - public static SelectUserId not(SelectUserId filter) { - return new SelectUserId() { - @Override - protected boolean accept(String userId) { - return !filter.accept(userId); - } - }; - } - - /** - * Filter that selects user-ids by the given
email
address. - * It returns user-ids which either contain the given
email
address as angle-bracketed string, - * or which equal the given
email
string exactly. - * - * @param email email - * @return filter - */ - public static SelectUserId byEmail(CharSequence email) { - return SelectUserId.or( - SelectUserId.exactMatch(email), - SelectUserId.containsEmailAddress(email) - ); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/package-info.java b/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/package-info.java deleted file mode 100644 index 5b70f412..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/util/selection/userid/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -/** - * UserID selection strategies. - */ -package org.pgpainless.util.selection.userid; diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 2fde469c..b219a739 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -31,7 +31,9 @@ import org.pgpainless.signature.subpackets.* import org.pgpainless.util.Passphrase import org.pgpainless.util.selection.userid.SelectUserId import java.util.* +import java.util.function.Predicate import javax.annotation.Nonnull +import kotlin.NoSuchElementException class SecretKeyRingEditor( var secretKeyRing: PGPSecretKeyRing, @@ -109,14 +111,22 @@ class SecretKeyRingEditor( return this } + @Deprecated("Use of SelectUserId class is deprecated.", replaceWith = ReplaceWith("removeUserId(protector, predicate)")) override fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { return revokeUserIds(selector, protector, RevocationAttributes.createCertificateRevocation() .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) .withoutDescription()) } + override fun removeUserId(protector: SecretKeyRingProtector, predicate: (String) -> Boolean): SecretKeyRingEditorInterface { + return revokeUserIds(protector, RevocationAttributes.createCertificateRevocation() + .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) + .withoutDescription(), + predicate) + } + override fun removeUserId(userId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { - return removeUserId(SelectUserId.exactMatch(userId), protector) + return removeUserId(protector) { uid -> userId == uid } } override fun replaceUserId(oldUserId: CharSequence, newUserId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { @@ -246,21 +256,23 @@ class SecretKeyRingEditor( } override fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { - return revokeUserIds(SelectUserId.exactMatch(sanitizeUserId(userId)), protector, callback) + return revokeUserIds(protector, callback, SelectUserId.exactMatch(sanitizeUserId(userId))) } - override fun revokeUserIds(selector: SelectUserId, protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): SecretKeyRingEditorInterface { - return revokeUserIds(selector, protector, object : RevocationSignatureSubpackets.Callback { + override fun revokeUserIds(protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes?, + predicate: (String) -> Boolean): SecretKeyRingEditorInterface { + return revokeUserIds(protector, object : RevocationSignatureSubpackets.Callback { override fun modifyHashedSubpackets(hashedSubpackets: RevocationSignatureSubpackets) { - if (revocationAttributes != null) { - hashedSubpackets.setRevocationReason(revocationAttributes) - } + if (revocationAttributes != null) hashedSubpackets.setRevocationReason(revocationAttributes) } - }) + }, predicate) } - override fun revokeUserIds(selector: SelectUserId, protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { - selector.selectUserIds(secretKeyRing).also { + override fun revokeUserIds(protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback?, + predicate: (String) -> Boolean): SecretKeyRingEditorInterface { + selectUserIds(predicate).also { if (it.isEmpty()) throw NoSuchElementException("No matching user-ids found on the key.") }.forEach { userId -> doRevokeUserId(userId, protector, callback) } return this @@ -348,7 +360,7 @@ class SecretKeyRingEditor( private fun sanitizeUserId(userId: CharSequence): CharSequence = // TODO: Further research how to sanitize user IDs. - // eg. what about newlines? + // e.g. what about newlines? userId.toString().trim() private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) = @@ -454,6 +466,9 @@ class SecretKeyRingEditor( }.build(secretKeyRing.publicKey) } + private fun selectUserIds(predicate: Predicate): List = + inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) } + private class WithKeyRingEncryptionSettingsImpl( private val editor: SecretKeyRingEditor, private val keyId: Long?, diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt index 45a09fb2..8a26161b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt @@ -4,11 +4,7 @@ package org.pgpainless.key.modification.secretkeyring -import org.bouncycastle.openpgp.PGPException -import org.bouncycastle.openpgp.PGPKeyPair -import org.bouncycastle.openpgp.PGPPublicKeyRing -import org.bouncycastle.openpgp.PGPSecretKeyRing -import org.bouncycastle.openpgp.PGPSignature +import org.bouncycastle.openpgp.* import org.pgpainless.algorithm.KeyFlag import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.generation.KeySpec @@ -23,6 +19,7 @@ import java.io.IOException import java.security.InvalidAlgorithmParameterException import java.security.NoSuchAlgorithmException import java.util.* +import java.util.function.Predicate interface SecretKeyRingEditorInterface { @@ -82,8 +79,25 @@ interface SecretKeyRingEditorInterface { * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ + @Deprecated("Use of SelectUserId class is deprecated.", + ReplaceWith("removeUserId(protector, predicate)")) @Throws(PGPException::class) - fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector) = + removeUserId(protector, selector) + + /** + * Convenience method to revoke selected user-ids using soft revocation signatures. + * The revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the user-id + * can be re-certified at a later point. + * + * @param protector protector to unlock the primary key + * @param predicate predicate to select user-ids for revocation + * @return the builder + * + * @throws PGPException in case we cannot generate a revocation signature for the user-id + */ + @Throws(PGPException::class) + fun removeUserId(protector: SecretKeyRingProtector, predicate: (String) -> Boolean): SecretKeyRingEditorInterface /** * Convenience method to revoke a single user-id using a soft revocation signature. @@ -342,9 +356,32 @@ interface SecretKeyRingEditorInterface { * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) + @Deprecated("Use of SelectUserId class is deprecated.", + ReplaceWith("revokeUserIds(protector, revocationAttributes, predicate)")) fun revokeUserIds(selector: SelectUserId, protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?): SecretKeyRingEditorInterface + revocationAttributes: RevocationAttributes?) = + revokeUserIds(protector, revocationAttributes, selector) + + /** + * Revoke all user-ids that match the provided [SelectUserId] filter. + * The provided [RevocationAttributes] will be set as reason for revocation in each + * revocation signature. + * + * Note: If you intend to re-certify these user-ids at a later point, make sure to choose + * a soft revocation reason. See [RevocationAttributes.Reason] for more information. + * + * @param protector protector to unlock the primary secret key + * @param revocationAttributes revocation attributes + * @param predicate to select user-ids for revocation + * @return builder + * + * @throws PGPException in case we cannot generate a revocation signature for the user-id + */ + @Throws(PGPException::class) + fun revokeUserIds(protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes?, + predicate: (String) -> Boolean): SecretKeyRingEditorInterface /** * Revoke all user-ids that match the provided [SelectUserId] filter. @@ -364,9 +401,34 @@ interface SecretKeyRingEditorInterface { * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) + @Deprecated("Use of SelectUserId class is deprecated.", + ReplaceWith("revokeUserIds(protector, callback, predicate)")) fun revokeUserIds(selector: SelectUserId, protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface + callback: RevocationSignatureSubpackets.Callback?) = + revokeUserIds(protector, callback, selector) + + /** + * Revoke all user-ids that match the provided [SelectUserId] filter. + * The provided [RevocationSignatureSubpackets.Callback] will be used to modify the + * revocation signatures subpackets. + * + * Note: If you intend to re-certify these user-ids at a later point, make sure to set + * a soft revocation reason in the revocation signatures hashed subpacket area using the callback. + * + * See [RevocationAttributes.Reason] for more information. + * + * @param protector protector to unlock the primary secret key + * @param callback callback to modify the revocations subpackets + * @param predicate to select user-ids for revocation + * @return builder + * + * @throws PGPException in case we cannot generate a revocation signature for the user-id + */ + @Throws(PGPException::class) + fun revokeUserIds(protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback?, + predicate: (String) -> Boolean): SecretKeyRingEditorInterface /** * Set the expiration date for the primary key of the key ring. diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt new file mode 100644 index 00000000..3c215d80 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.util.selection.userid + +import org.bouncycastle.openpgp.PGPKeyRing +import org.pgpainless.PGPainless +import java.util.function.Predicate + +abstract class SelectUserId : Predicate, (String) -> Boolean { + + /** + * Legacy glue code to forward accept() calls to invoke() instead. + */ + @Deprecated("Use invoke() instead.", ReplaceWith("invoke(userId)")) + protected fun accept(userId: String): Boolean = invoke(userId) + + override fun test(userId: String): Boolean = invoke(userId) + + companion object { + + /** + * Filter for user-ids which match the given [query] exactly. + * + * @param query query + * @return filter + */ + @JvmStatic + fun exactMatch(query: CharSequence) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + userId == query + } + + /** + * Filter for user-ids which start with the given [substring]. + * + * @param substring substring + * @return filter + */ + @JvmStatic + fun startsWith(substring: CharSequence) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + userId.startsWith(substring) + } + + /** + * Filter for user-ids which contain the given [substring]. + * + * @param substring query + * @return filter + */ + @JvmStatic + fun containsSubstring(substring: CharSequence) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + userId.contains(substring) + } + + /** + * Filter for user-ids which contain the given [email] address. + * Note: This only accepts user-ids which properly have the email address surrounded by angle brackets. + * + * The argument [email] can both be a plain email address (`foo@bar.baz`), + * or surrounded by angle brackets (``), the result of the filter will be the same. + * + * @param email email address + * @return filter + */ + @JvmStatic + fun containsEmailAddress(email: CharSequence) = + if (email.startsWith('<') && email.endsWith('>')) + containsSubstring(email) + else + containsSubstring("<$email>") + + @JvmStatic + fun byEmail(email: CharSequence) = or(exactMatch(email), containsEmailAddress(email)) + + @JvmStatic + fun validUserId(keyRing: PGPKeyRing) = object : SelectUserId() { + private val info = PGPainless.inspectKeyRing(keyRing) + override fun invoke(userId: String): Boolean = + info.isUserIdValid(userId) + } + + @JvmStatic + fun and(vararg filters: SelectUserId) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + filters.all { it.invoke(userId) } + } + + @JvmStatic + fun or(vararg filters: SelectUserId) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + filters.any { it.invoke(userId) } + } + + @JvmStatic + fun not(filter: SelectUserId) = object : SelectUserId() { + override fun invoke(userId: String): Boolean = + !filter.invoke(userId) + } + } +} \ No newline at end of file diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/selection/userid/SelectUserIdTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/selection/userid/SelectUserIdTest.java index 3a49247c..99a0c87c 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/selection/userid/SelectUserIdTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/selection/userid/SelectUserIdTest.java @@ -4,6 +4,15 @@ package org.pgpainless.util.selection.userid; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.junit.jupiter.api.Test; @@ -11,20 +20,10 @@ import org.pgpainless.PGPainless; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.util.UserId; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - public class SelectUserIdTest { @Test - public void testSelectUserIds() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + public void testSelectUserIds() throws PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() .simpleEcKeyRing(""); secretKeys = PGPainless.modifyKeyRing(secretKeys) @@ -34,48 +33,53 @@ public class SelectUserIdTest { SecretKeyRingProtector.unprotectedKeys()) .done(); - List validEmail = SelectUserId.and( + List userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds(); + List validEmail = userIds.stream().filter(SelectUserId.and( SelectUserId.validUserId(secretKeys), SelectUserId.containsEmailAddress("alice@wonderland.lit") - ).selectUserIds(secretKeys); + )).collect(Collectors.toList()); assertEquals(Collections.singletonList(""), validEmail); - List startsWithAlice = SelectUserId.startsWith("Alice").selectUserIds(secretKeys); + List startsWithAlice = userIds.stream().filter(SelectUserId.startsWith("Alice")).collect(Collectors.toList()); assertEquals(Collections.singletonList("Alice Liddell "), startsWithAlice); - List exactMatch = SelectUserId.or( + List exactMatch = userIds.stream().filter(SelectUserId.or( SelectUserId.exactMatch(""), SelectUserId.startsWith("Not Found") - ).selectUserIds(secretKeys); + )).collect(Collectors.toList()); assertEquals(Collections.singletonList(""), exactMatch); } @Test - public void testContainsSubstring() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + public void testContainsSubstring() throws PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("wine drinker"); secretKeys = PGPainless.modifyKeyRing(secretKeys) .addUserId("this is not a quine", SecretKeyRingProtector.unprotectedKeys()) .addUserId("this is not a crime", SecretKeyRingProtector.unprotectedKeys()) .done(); - List containSubstring = SelectUserId.containsSubstring("ine") - .selectUserIds(secretKeys); + List userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds(); + + List containSubstring = userIds.stream().filter(SelectUserId.containsSubstring("ine")).collect(Collectors.toList()); assertEquals(Arrays.asList("wine drinker", "this is not a quine"), containSubstring); } @Test - public void testContainsEmailAddress() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + public void testContainsEmailAddress() { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("Alice "); + List userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds(); - assertEquals("Alice ", SelectUserId.containsEmailAddress("alice@wonderland.lit").firstMatch(secretKeys)); - assertEquals("Alice ", SelectUserId.containsEmailAddress("").firstMatch(secretKeys)); + assertEquals("Alice ", userIds.stream().filter( + SelectUserId.containsEmailAddress("alice@wonderland.lit")).findFirst().get()); + assertEquals("Alice ", userIds.stream().filter( + SelectUserId.containsEmailAddress("")).findFirst().get()); - assertNull(SelectUserId.containsEmailAddress("mad@hatter.lit").firstMatch(secretKeys)); + assertFalse(userIds.stream().anyMatch(SelectUserId.containsEmailAddress("mad@hatter.lit"))); } @Test - public void testAndOrNot() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + public void testAndOrNot() throws PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("Alice "); secretKeys = PGPainless.modifyKeyRing(secretKeys) .addUserId("Alice ", SecretKeyRingProtector.unprotectedKeys()) @@ -83,34 +87,32 @@ public class SelectUserIdTest { .addUserId("Crazy Girl ", SecretKeyRingProtector.unprotectedKeys()) .done(); - List or = SelectUserId.or( + List userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds(); + + List or = userIds.stream().filter(SelectUserId.or( SelectUserId.containsEmailAddress("alice@wonderland.lit"), - SelectUserId.startsWith("Alice")) - .selectUserIds(secretKeys); + SelectUserId.startsWith("Alice"))).collect(Collectors.toList()); assertEquals(Arrays.asList("Alice ", "Alice ", "Crazy Girl "), or); - List and = SelectUserId.and( + List and = userIds.stream().filter(SelectUserId.and( SelectUserId.containsEmailAddress("alice@wonderland.lit"), - SelectUserId.startsWith("Alice")) - .selectUserIds(secretKeys); + SelectUserId.startsWith("Alice"))).collect(Collectors.toList()); assertEquals(Collections.singletonList("Alice "), and); - List not = SelectUserId.not( - SelectUserId.startsWith("Alice")) - .selectUserIds(secretKeys); + List not = userIds.stream().filter(SelectUserId.not( + SelectUserId.startsWith("Alice"))).collect(Collectors.toList()); assertEquals(Arrays.asList("", "Crazy Girl "), not); } @Test - public void testFirstMatch() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + public void testFirstMatch() throws PGPException { PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleEcKeyRing("First UserID"); secretKeys = PGPainless.modifyKeyRing(secretKeys) .addUserId("Second UserID", SecretKeyRingProtector.unprotectedKeys()) .done(); - assertEquals("First UserID", SelectUserId.validUserId(secretKeys).firstMatch(secretKeys)); - assertEquals("Second UserID", SelectUserId.containsSubstring("Second").firstMatch( - PGPainless.inspectKeyRing(secretKeys).getUserIds() - )); + List userIds = PGPainless.inspectKeyRing(secretKeys).getValidUserIds(); + assertEquals("First UserID", userIds.stream().filter(SelectUserId.validUserId(secretKeys)).findFirst().get()); + assertEquals("Second UserID", userIds.stream().filter(SelectUserId.containsSubstring("Second")).findFirst().get()); } @Test