From d796c76ce61d1e323d196a2541b81a76a05d7c84 Mon Sep 17 00:00:00 2001 From: JJ Date: Mon, 10 Jul 2017 07:34:02 -0500 Subject: [PATCH 1/2] METRON-539: added HASH function for stellar. METRON-539: Adding GET_HASHES_AVAILABLE function and addressed suggestions. --- metron-stellar/stellar-common/README.md | 118 +++++----- metron-stellar/stellar-common/pom.xml | 5 + .../common/utils/hashing/DefaultHasher.java | 97 +++++++++ .../stellar/common/utils/hashing/Hasher.java | 35 +++ .../stellar/dsl/functions/HashFunctions.java | 86 ++++++++ .../utils/hashing/DefaultHasherTest.java | 59 +++++ .../dsl/functions/HashFunctionsTest.java | 205 ++++++++++++++++++ 7 files changed, 553 insertions(+), 52 deletions(-) create mode 100644 metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java create mode 100644 metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java create mode 100644 metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java create mode 100644 metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java create mode 100644 metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java diff --git a/metron-stellar/stellar-common/README.md b/metron-stellar/stellar-common/README.md index f12c77d84c..a25c8313e6 100644 --- a/metron-stellar/stellar-common/README.md +++ b/metron-stellar/stellar-common/README.md @@ -101,62 +101,63 @@ In the core language functions, we support basic functional programming primitiv ## Stellar Core Functions -| | -| ---------- | -| [ `ABS`](../../metron-analytics/metron-statistics#abs) | -| [ `APPEND_IF_MISSING`](#append_if_missing) | -| [ `BIN`](../../metron-analytics/metron-statistics#bin) | -| [ `BLOOM_ADD`](#bloom_add) | -| [ `BLOOM_EXISTS`](#bloom_exists) | -| [ `BLOOM_INIT`](#bloom_init) | -| [ `BLOOM_MERGE`](#bloom_merge) | +| | +| ---------- | +| [ `ABS`](../../metron-analytics/metron-statistics#abs) | +| [ `APPEND_IF_MISSING`](#append_if_missing) | +| [ `BIN`](../../metron-analytics/metron-statistics#bin) | +| [ `BLOOM_ADD`](#bloom_add) | +| [ `BLOOM_EXISTS`](#bloom_exists) | +| [ `BLOOM_INIT`](#bloom_init) | +| [ `BLOOM_MERGE`](#bloom_merge) | | [ `CEILING`](#ceiling) | | [ `COS`](#cos) | -| [ `CHOP`](#chop) | -| [ `CHOMP`](#chomp) | -| [ `COUNT_MATCHES`](#count_matches) | -| [ `DAY_OF_MONTH`](#day_of_month) | -| [ `DAY_OF_WEEK`](#day_of_week) | -| [ `DAY_OF_YEAR`](#day_of_year) | -| [ `DECODE`](#decode) | -| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains) | -| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld) | -| [ `DOMAIN_TO_TLD`](#domain_to_tld) | -| [ `ENCODE`](#encode) | -| [ `ENDS_WITH`](#ends_with) | -| [ `ENRICHMENT_EXISTS`](#enrichment_exists) | -| [ `ENRICHMENT_GET`](#enrichment_get) | +| [ `CHOP`](#chop) | +| [ `CHOMP`](#chomp) | +| [ `COUNT_MATCHES`](#count_matches) | +| [ `DAY_OF_MONTH`](#day_of_month) | +| [ `DAY_OF_WEEK`](#day_of_week) | +| [ `DAY_OF_YEAR`](#day_of_year) | +| [ `DECODE`](#decode) | +| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains) | +| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld) | +| [ `DOMAIN_TO_TLD`](#domain_to_tld) | +| [ `ENCODE`](#encode) | +| [ `ENDS_WITH`](#ends_with) | +| [ `ENRICHMENT_EXISTS`](#enrichment_exists) | +| [ `ENRICHMENT_GET`](#enrichment_get) | | [ `EXP`](#exp) | -| [ `FILL_LEFT`](#fill_left) | -| [ `FILL_RIGHT`](#fill_right) | -| [ `FILTER`](#filter) | +| [ `FILL_LEFT`](#fill_left) | +| [ `FILL_RIGHT`](#fill_right) | +| [ `FILTER`](#filter) | | [ `FLOOR`](#floor) | -| [ `FORMAT`](#format) | -| [ `HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality) | -| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init) | -| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge) | -| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer) | -| [ `GEO_GET`](#geo_get) | -| [ `GET`](#get) | -| [ `GET_FIRST`](#get_first) | -| [ `GET_LAST`](#get_last) | -| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings) | -| [ `IN_SUBNET`](#in_subnet) | -| [ `IS_DATE`](#is_date) | -| [ `IS_ENCODING`](#is_encoding) | -| [ `IS_DOMAIN`](#is_domain) | -| [ `IS_EMAIL`](#is_email) | -| [ `IS_EMPTY`](#is_empty) | -| [ `IS_INTEGER`](#is_integer) | -| [ `IS_IP`](#is_ip) | -| [ `IS_URL`](#is_url) | -| [ `JOIN`](#join) | -| [ `KAFKA_GET`](#kafka_get) | -| [ `KAFKA_PROPS`](#kafka_props) | -| [ `KAFKA_PUT`](#kafka_put) | -| [ `KAFKA_TAIL`](#kafka_tail) | -| [ `LENGTH`](#length) | -| [ `LIST_ADD`](#list_add) | +| [ `FORMAT`](#format) | +| [ `GEO_GET`](#geo_get) | +| [ `GET`](#get) | +| [ `GET_FIRST`](#get_first) | +| [ `GET_LAST`](#get_last) | +| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings) | +| [ `HASH`](#hash) | +| [ `HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality) | +| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init) | +| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge) | +| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer) | +| [ `IN_SUBNET`](#in_subnet) | +| [ `IS_DATE`](#is_date) | +| [ `IS_ENCODING`](#is_encoding) | +| [ `IS_DOMAIN`](#is_domain) | +| [ `IS_EMAIL`](#is_email) | +| [ `IS_EMPTY`](#is_empty) | +| [ `IS_INTEGER`](#is_integer) | +| [ `IS_IP`](#is_ip) | +| [ `IS_URL`](#is_url) | +| [ `JOIN`](#join) | +| [ `KAFKA_GET`](#kafka_get) | +| [ `KAFKA_PROPS`](#kafka_props) | +| [ `KAFKA_PUT`](#kafka_put) | +| [ `KAFKA_TAIL`](#kafka_tail) | +| [ `LENGTH`](#length) | +| [ `LIST_ADD`](#list_add) | | [ `LOG2`](#log2) | | [ `LOG10`](#log10) | | [ `LN`](#ln) | @@ -431,6 +432,10 @@ In the core language functions, we support basic functional programming primitiv * Input: * input - List * Returns: First element of the list + +### `GET_HASHES_AVAILABLE` + * Description: Will return all available hashing algorithms available to 'HASH'. + * Returns: A list containing all supported hashing algorithms. ### `GET_LAST` * Description: Returns the last element of the list @@ -438,6 +443,15 @@ In the core language functions, we support basic functional programming primitiv * input - List * Returns: Last element of the list +### `HASH` + * Description: Hashes a given value using the given hashing algorithm and returns a hex encoded string. + * Input: + * toHash - value to hash. + * hashType - A valid string representation of a hashing algorithm. See 'GET_HASHES_AVAILABLE'. + * Returns: A hex encoded string of a hashed value using the given algorithm. If 'hashType' is null + then '00', padded to the necessary length, will be returned. If 'toHash' is not able to be hashed or + 'hashType' is null then null is returned. + ### `IN_SUBNET` * Description: Returns true if an IP is within a subnet range. * Input: diff --git a/metron-stellar/stellar-common/pom.xml b/metron-stellar/stellar-common/pom.xml index 2f4cb6e4cd..61549698fa 100644 --- a/metron-stellar/stellar-common/pom.xml +++ b/metron-stellar/stellar-common/pom.xml @@ -101,6 +101,11 @@ commons-validator 1.6 + + commons-codec + commons-codec + 1.10 + org.apache.curator curator-client diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java new file mode 100644 index 0000000000..c950a19bc2 --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.metron.stellar.common.utils.hashing; + +import org.apache.commons.codec.BinaryEncoder; +import org.apache.commons.codec.EncoderException; +import org.apache.commons.lang3.SerializationUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class DefaultHasher implements Hasher { + + private String algorithm; + private BinaryEncoder encoder; + private Charset charset; + + /** + * Builds a utility to hash values based on a given algorithm. + * @param algorithm The algorithm used when hashing a value. + * @param encoder The encoder to use to encode the hashed value. + * @param charset The charset that will be used during hashing and encoding. + * @see java.security.Security + * @see java.security.MessageDigest + */ + public DefaultHasher(final String algorithm, final BinaryEncoder encoder, final Charset charset) { + this.algorithm = algorithm; + this.encoder = encoder; + this.charset = charset; + } + + /** + * Builds a utility to hash values based on a given algorithm. Uses {@link StandardCharsets#UTF_8} for encoding. + * @param algorithm The algorithm used when hashing a value. + * @param encoder The encoder to use to encode the hashed value. + * @see java.security.Security + * @see java.security.MessageDigest + */ + public DefaultHasher(final String algorithm, final BinaryEncoder encoder) { + this.algorithm = algorithm; + this.encoder = encoder; + this.charset = StandardCharsets.UTF_8; + } + + /** + * {@inheritDoc} + * + * Returns a hash which has been encoded using the supplied encoder. If input is null then a string + * containing all '0' will be returned. The length of the string is determined by the hashing algorithm + * used. + * @param toHash The value to hash. + * @return A hash of {@code toHash} that has been encoded. + * @throws EncoderException If unable to encode the hash then this exception occurs. + * @throws NoSuchAlgorithmException If the supplied algorithm is not known. + */ + @Override + public String getHash(final Object toHash) throws EncoderException, NoSuchAlgorithmException { + final MessageDigest messageDigest = MessageDigest.getInstance(algorithm); + + if (toHash == null) { + return StringUtils.repeat("00", messageDigest.getDigestLength()); + } else if (toHash instanceof String) { + return getHash(messageDigest, toHash.toString().getBytes(charset)); + } else if (toHash instanceof Serializable) { + final byte[] serialized = SerializationUtils.serialize((Serializable) toHash); + return getHash(messageDigest, serialized); + } + + return null; + } + + private String getHash(final MessageDigest messageDigest, final byte[] toHash) throws EncoderException { + messageDigest.update(toHash); + final byte[] encode = encoder.encode(messageDigest.digest()); + + return new String(encode, charset); + } +} diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java new file mode 100644 index 0000000000..08e8f72f7d --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.metron.stellar.common.utils.hashing; + +import org.apache.commons.codec.EncoderException; + +import java.security.NoSuchAlgorithmException; + +public interface Hasher { + + /** + * Returns an encoded string representation of the hash value of the input. It is expected that + * this implementation does throw exceptions when the input is null. + * @param toHash The value to hash. + * @return A hash of {@code toHash} that has been encoded. + * @throws EncoderException If unable to encode the hash then this exception occurs. + * @throws NoSuchAlgorithmException If the supplied algorithm is not known. + */ + String getHash(final Object toHash) throws EncoderException, NoSuchAlgorithmException; +} diff --git a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java new file mode 100644 index 0000000000..5e59b6e028 --- /dev/null +++ b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.metron.stellar.dsl.functions; + +import org.apache.commons.codec.EncoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.metron.stellar.common.utils.hashing.DefaultHasher; +import org.apache.metron.stellar.dsl.BaseStellarFunction; +import org.apache.metron.stellar.dsl.Stellar; + +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.ArrayList; +import java.util.List; + +public class HashFunctions { + + @Stellar( + name = "GET_HASHES_AVAILABLE", + description = "Will return all available hashing algorithms available to 'HASH'.", + returns = "A list containing all supported hashing algorithms." + ) + public static class ListSupportedHashTypes extends BaseStellarFunction { + + @Override + public List apply(final List args) { + if (args == null || args.size() != 0) { + throw new IllegalArgumentException("Invalid call. This function does not expect any arguments."); + } + + return new ArrayList<>(Security.getAlgorithms("MessageDigest")); + } + } + + @Stellar( + name = "HASH", + description = "Hashes a given value using the given hashing algorithm and returns a hex encoded string.", + params = { + "toHash - value to hash.", + "hashType - A valid string representation of a hashing algorithm. See 'GET_HASHES_AVAILABLE'.", + }, + returns = "A hex encoded string of a hashed value using the given algorithm. If 'hashType' is null " + + "then '00', padded to the necessary length, will be returned. If 'toHash' is not able to be hashed or " + + "'hashType' is null then null is returned." + ) + public static class Hash extends BaseStellarFunction { + + @Override + public Object apply(final List args) { + if (args == null || args.size() != 2) { + throw new IllegalArgumentException("Invalid number of arguments: " + (args == null ? 0 : args.size())); + } + + final Object toHash = args.get(0); + final Object hashType = args.get(1); + + if (hashType == null) { + return null; + } + + try { + return new DefaultHasher(hashType.toString(), new Hex(StandardCharsets.UTF_8)).getHash(toHash); + } catch (final EncoderException e) { + return null; + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Invalid hash type: " + hashType.toString()); + } + } + } +} diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java new file mode 100644 index 0000000000..6ab21845ed --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.metron.stellar.common.utils.hashing; + +import org.apache.commons.codec.BinaryEncoder; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + + +public class DefaultHasherTest { + private Charset charset = StandardCharsets.UTF_8; + private BinaryEncoder encoder = new Hex(charset); + + @Test + public void getHashAsHexOfNullValueReturnsPadded00() throws Exception { + assertEquals(StringUtils.repeat("00", 16), new DefaultHasher("md5", encoder).getHash(null)); + assertEquals(StringUtils.repeat("00", 32), new DefaultHasher("sha-256", encoder).getHash(null)); + } + + @Test + public void nonSerializableShouldReturnNull() throws Exception { + assertNull(new DefaultHasher("md5", encoder, charset).getHash(new Object())); + } + + @Test + public void hashingStringWithEmptyString() throws Exception { + assertEquals("d41d8cd98f00b204e9800998ecf8427e", new DefaultHasher("md5", encoder).getHash("")); + } + + @Test + public void hashingSerializableObject() throws Exception { + final Collection serializable = Collections.emptyList(); + assertEquals("ef5e8c8d27af3a953b4674065c99a52a", new DefaultHasher("md5", encoder, charset).getHash(serializable)); + } +} \ No newline at end of file diff --git a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java new file mode 100644 index 0000000000..31bc6d3d77 --- /dev/null +++ b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.metron.stellar.dsl.functions; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang.SerializationUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.apache.metron.stellar.common.utils.StellarProcessorUtils.run; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class HashFunctionsTest { + static final Hex HEX = new Hex(StandardCharsets.UTF_8); + final HashFunctions.ListSupportedHashTypes listSupportedHashTypes = new HashFunctions.ListSupportedHashTypes(); + final HashFunctions.Hash hash = new HashFunctions.Hash(); + + @Test(expected = IllegalArgumentException.class) + public void nullArgumentsShouldFail() throws Exception { + listSupportedHashTypes.apply(null); + } + + @Test(expected = IllegalArgumentException.class) + public void getSupportedHashAlgorithmsCalledWithParametersShouldFail() throws Exception { + listSupportedHashTypes.apply(Collections.singletonList("bogus")); + } + + @Test + public void listSupportedHashTypesReturnsAtMinimumTheHashingAlgorithmsThatMustBeSupported() throws Exception { + final List requiredAlgorithmsByJava = Arrays.asList("MD5", "SHA", "SHA-256"); // These are required for all Java platforms (see java.security.MessageDigest). Note: SHA is SHA-1 + final Collection supportedHashes = listSupportedHashTypes.apply(Collections.emptyList()); + requiredAlgorithmsByJava.forEach(a -> assertTrue(supportedHashes.contains(a))); + } + + @Test(expected = IllegalArgumentException.class) + public void nullArgumentListShouldThrowException() throws Exception { + hash.apply(null); + } + + @Test(expected = IllegalArgumentException.class) + public void emptyArgumentListShouldThrowException() throws Exception { + hash.apply(Collections.emptyList()); + } + + @Test(expected = IllegalArgumentException.class) + public void singleArgumentListShouldThrowException() throws Exception { + hash.apply(Collections.singletonList("some value.")); + } + + @Test(expected = IllegalArgumentException.class) + public void argumentListWithMoreThanTwoValuesShouldThrowException3() throws Exception { + hash.apply(Arrays.asList("1", "2", "3")); + } + + @Test(expected = IllegalArgumentException.class) + public void argumentListWithMoreThanTwoValuesShouldThrowException4() throws Exception { + hash.apply(Arrays.asList("1", "2", "3", "4")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidAlgorithmArgumentShouldThrowException() throws Exception { + hash.apply(Arrays.asList("value to hash", "invalidAlgorithm")); + } + + @Test + public void invalidNullAlgorithmArgumentShouldReturnNull() throws Exception { + assertNull(hash.apply(Arrays.asList("value to hash", null))); + } + + @Test + public void nullInputForValueToHashShouldReturnHashedEncodedValueOf0x00() throws Exception { + assertEquals(StringUtils.repeat('0', 32), hash.apply(Arrays.asList(null, "md5"))); + } + + @Test + public void nullInputForValueToHashShouldReturnHashedEncodedValueOf0x00InDirectStellarCall() throws Exception { + final String algorithm = "'md5'"; + final Map variables = new HashMap<>(); + variables.put("toHash", null); + + assertEquals(StringUtils.repeat('0', 32), run("HASH(toHash, " + algorithm + ")", variables)); + } + + @Test + public void allAlgorithmsForMessageDigestShouldBeAbleToHash() throws Exception { + final String valueToHash = "My value to hash"; + final Set algorithms = Security.getAlgorithms("MessageDigest"); + + algorithms.forEach(algorithm -> { + try { + final MessageDigest expected = MessageDigest.getInstance(algorithm); + expected.update(valueToHash.getBytes(StandardCharsets.UTF_8)); + + assertEquals(expectedHexString(expected), hash.apply(Arrays.asList(valueToHash, algorithm))); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void allAlgorithmsForMessageDigestShouldBeAbleToHashDirectStellarCall() throws Exception { + final String valueToHash = "My value to hash"; + final Set algorithms = Security.getAlgorithms("MessageDigest"); + + algorithms.forEach(algorithm -> { + try { + final Object actual = run("HASH('" + valueToHash + "', '" + algorithm + "')", Collections.emptyMap()); + + final MessageDigest expected = MessageDigest.getInstance(algorithm); + expected.update(valueToHash.getBytes(StandardCharsets.UTF_8)); + + assertEquals(expectedHexString(expected), actual); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void nonStringValueThatIsSerializableHashesSuccessfully() throws Exception { + final String algorithm = "'md5'"; + final String valueToHash = "'My value to hash'"; + final Serializable input = (Serializable) Collections.singletonList(valueToHash); + + final MessageDigest expected = MessageDigest.getInstance(algorithm.replace("'", "")); + expected.update(SerializationUtils.serialize(input)); + + final Map variables = new HashMap<>(); + variables.put("toHash", input); + + assertEquals(expectedHexString(expected), run("HASH(toHash, " + algorithm + ")", variables)); + } + + @Test + public void callingHashFunctionsWithVariablesAsInputHashesSuccessfully() throws Exception { + final String algorithm = "md5"; + final String valueToHash = "'My value to hash'"; + final Serializable input = (Serializable) Collections.singletonList(valueToHash); + + final MessageDigest expected = MessageDigest.getInstance(algorithm); + expected.update(SerializationUtils.serialize(input)); + + final Map variables = new HashMap<>(); + variables.put("toHash", input); + variables.put("hashType", algorithm); + + assertEquals(expectedHexString(expected), run("HASH(toHash, hashType)", variables)); + } + + @Test + public void callingHashFunctionWhereOnlyHashTypeIsAVariableHashesSuccessfully() throws Exception { + final String algorithm = "md5"; + final String valueToHash = "'My value to hash'"; + + final MessageDigest expected = MessageDigest.getInstance(algorithm); + expected.update(valueToHash.replace("'", "").getBytes(StandardCharsets.UTF_8)); + + final Map variables = new HashMap<>(); + variables.put("hashType", algorithm); + + assertEquals(expectedHexString(expected), run("HASH(" + valueToHash + ", hashType)", variables)); + } + + @Test + public void aNonNullNonSerializableObjectReturnsAValueOfNull() throws Exception { + final Map variables = new HashMap<>(); + variables.put("toHash", new Object()); + assertNull(run("HASH(toHash, 'md5')", variables)); + } + + private String expectedHexString(MessageDigest expected) { + return new String(HEX.encode(expected.digest()), StandardCharsets.UTF_8); + } +} From 64c46b360e63ae5782230b1475102052c177f843 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 28 Jul 2017 08:39:51 -0500 Subject: [PATCH 2/2] Removed duplicate dependency. --- metron-stellar/stellar-common/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/metron-stellar/stellar-common/pom.xml b/metron-stellar/stellar-common/pom.xml index 61549698fa..2f4cb6e4cd 100644 --- a/metron-stellar/stellar-common/pom.xml +++ b/metron-stellar/stellar-common/pom.xml @@ -101,11 +101,6 @@ commons-validator 1.6 - - commons-codec - commons-codec - 1.10 - org.apache.curator curator-client