From 086e8381d8235db912d935def6491ef321c75a9b Mon Sep 17 00:00:00 2001 From: Piotr Jaszczyk Date: Thu, 30 May 2019 13:04:36 +0200 Subject: Update dependencies and enhance crypt-password lib Change-Id: I7fe56e43d7ff0ac9b50708a6abe2163f7ddf8904 Issue-ID: DCAEGEN2-1542 Signed-off-by: Piotr Jaszczyk --- security/crypt-password/pom.xml | 98 +++++++++++++--------- .../sdk/security/CharsFromStreamReader.java | 62 ++++++++++++++ .../services/sdk/security/CryptPassword.java | 33 ++++++-- .../services/sdk/security/DecodePassword.java | 34 -------- .../services/sdk/security/EncodePassword.java | 76 +++++++++++++++++ .../dcaegen2/services/sdk/security/ExitCode.java | 31 +++++++ .../sdk/security/CharsFromStreamReaderTest.java | 57 +++++++++++++ .../services/sdk/security/CryptPasswordTest.java | 55 ++++++++++++ 8 files changed, 364 insertions(+), 82 deletions(-) create mode 100644 security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReader.java delete mode 100644 security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/DecodePassword.java create mode 100644 security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/EncodePassword.java create mode 100644 security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/ExitCode.java create mode 100644 security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReaderTest.java create mode 100644 security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CryptPasswordTest.java (limited to 'security') diff --git a/security/crypt-password/pom.xml b/security/crypt-password/pom.xml index 2e37c151..299aced7 100644 --- a/security/crypt-password/pom.xml +++ b/security/crypt-password/pom.xml @@ -1,52 +1,70 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - - org.onap.dcaegen2.services.sdk.security - dcaegen2-services-sdk-security - 1.2.0-SNAPSHOT - - 4.0.0 + + org.onap.dcaegen2.services.sdk.security + dcaegen2-services-sdk-security + 1.2.0-SNAPSHOT + + 4.0.0 - org.onap.dcaegen2.services.sdk.security.crypt - crypt-password + org.onap.dcaegen2.services.sdk.security.crypt + crypt-password - Security :: Crypt Password - DMaaP Security Module - jar + Security :: Crypt Password + DMaaP Security Module + jar - - - org.springframework.security - spring-security-crypto - 3.1.0.RELEASE - - + + + org.springframework.security + spring-security-crypto + + + org.junit.jupiter + junit-jupiter-api + + + org.assertj + assertj-core + + + org.slf4j + jcl-over-slf4j + runtime + + + ch.qos.logback + logback-classic + runtime + + - - org.apache.maven.plugins - maven-shade-plugin - 3.2.1 - - - package - - shade - - - - - org.onap.dcaegen2.services.sdk.security.DecodePassword - - - - - - + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + + + org.onap.dcaegen2.services.sdk.security.EncodePassword + + + + + + diff --git a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReader.java b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReader.java new file mode 100644 index 00000000..1ea18cc7 --- /dev/null +++ b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReader.java @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.CharBuffer; + +final class CharsFromStreamReader { + + public static final int END_OF_STREAM = -1; + private final int maxLength; + + CharsFromStreamReader(int maxLength) { + this.maxLength = maxLength; + } + + CharSequence readPasswordFromStdIn() throws IOException { + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) { + return readPassword(bufferedReader); + } + } + + CharSequence readPassword(BufferedReader bufferedReader) throws IOException { + final CharBuffer charBuffer = CharBuffer.allocate(maxLength); + if (readAllChars(charBuffer, bufferedReader)) { + charBuffer.flip(); + return charBuffer.asReadOnlyBuffer(); + } else { + throw new IOException( + "Input exceeds maximum supported length of " + maxLength + " characters"); + } + } + + private boolean readAllChars(CharBuffer charBuffer, BufferedReader bufferedReader) throws IOException { + int readChars = 0; + while (readChars != END_OF_STREAM && charBuffer.remaining() > 0) { + readChars = bufferedReader.read(charBuffer); + } + // true when all characters were read + return readChars == -1; + } +} diff --git a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CryptPassword.java b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CryptPassword.java index 6ca78a01..4b16c9e2 100644 --- a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CryptPassword.java +++ b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/CryptPassword.java @@ -21,15 +21,32 @@ package org.onap.dcaegen2.services.sdk.security; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -public class CryptPassword { +/** + * Class for encoding passwords using BCrypt algorithm. + */ +public final class CryptPassword { - private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - public String decode(String arg) { - return encoder.encode(arg); - } + /** + * Encode the raw password. + * + * @param rawPassword raw password to be encoded + * @return encoded password + */ + public String encode(CharSequence rawPassword) { + return encoder.encode(rawPassword); + } - public boolean matches(String rawPassword, String encodedPassword){ - return encoder.matches(rawPassword,encodedPassword); - } + /** + * Verify the encoded password matches the submitted raw password. Returns true if the passwords match, false if + * they do not. + * + * @param rawPassword the raw password to encode and match + * @param encodedPassword the encoded password to compare with + * @return true if the raw password, after encoding, matches the encoded password + */ + public boolean matches(CharSequence rawPassword, String encodedPassword) { + return encoder.matches(rawPassword, encodedPassword); + } } diff --git a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/DecodePassword.java b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/DecodePassword.java deleted file mode 100644 index 85412eb6..00000000 --- a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/DecodePassword.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * DCAEGEN2-SERVICES-SDK - * ================================================================================ - * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed 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. - * ============LICENSE_END========================================================= - */ -package org.onap.dcaegen2.services.sdk.security; - -class DecodePassword { - - private static CryptPassword cryptPassword = new CryptPassword(); - - public static void main(String[] args) { - - try { - System.out.println(cryptPassword.decode(args[0])); - }catch(Exception e){ - System.out.println("Param to crypt is required !"); - } - } -} diff --git a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/EncodePassword.java b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/EncodePassword.java new file mode 100644 index 00000000..77843816 --- /dev/null +++ b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/EncodePassword.java @@ -0,0 +1,76 @@ +/* + * ============LICENSE_START======================================================= + * DCAEGEN2-SERVICES-SDK + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ +package org.onap.dcaegen2.services.sdk.security; + +import java.io.IOException; + +class EncodePassword { + + private static final int MAX_PASSWORD_LENGTH = 64 * 1024; + private static final int ARGS_LENGTH_PASSWORD_PROVIDED = 1; + private CryptPassword cryptPassword = new CryptPassword(); + private CharsFromStreamReader charsFromStreamReader = new CharsFromStreamReader(MAX_PASSWORD_LENGTH); + + public static void main(String[] args) { + new EncodePassword().run(args); + } + + public void run(String[] args) { + try { + encodeRawInput(readPassword(args)); + } catch (IOException ex) { + printErrorAndExit(ExitCode.IO_ERROR, "Error while reading the password: " + ex.getMessage()); + } + } + + private void encodeRawInput(CharSequence rawPassword) { + if (rawPassword == null || rawPassword.length() == 0) { + printErrorAndExit(ExitCode.INVALID_PASSWORD, "Password cannot be empty"); + } else { + printWarningIfContainsEndlChars(rawPassword); + printResult(cryptPassword.encode(rawPassword)); + } + } + + private void printWarningIfContainsEndlChars(CharSequence rawPassword) { + if (rawPassword.chars().anyMatch(ch -> ch == '\n' || ch == '\r')) { + printWarning("Warning: Password contains end of lines characters."); + } + } + + private CharSequence readPassword(String[] args) throws IOException { + return args.length >= ARGS_LENGTH_PASSWORD_PROVIDED + ? args[0] + : charsFromStreamReader.readPasswordFromStdIn(); + } + + private void printWarning(String msg) { + System.err.println(msg); + } + + private void printErrorAndExit(ExitCode exitCode, String msg) { + System.err.println(msg); + System.exit(exitCode.value); + } + + private void printResult(String encodedPassword) { + System.out.println(encodedPassword); + } +} diff --git a/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/ExitCode.java b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/ExitCode.java new file mode 100644 index 00000000..51141eed --- /dev/null +++ b/security/crypt-password/src/main/java/org/onap/dcaegen2/services/sdk/security/ExitCode.java @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security; + +enum ExitCode { + INVALID_PASSWORD(1), IO_ERROR(2); + + final int value; + + ExitCode(int value) { + this.value = value; + } +} \ No newline at end of file diff --git a/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReaderTest.java b/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReaderTest.java new file mode 100644 index 00000000..2cfaa291 --- /dev/null +++ b/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CharsFromStreamReaderTest.java @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import org.junit.jupiter.api.Test; + +class CharsFromStreamReaderTest { + private static final int MAX_LENGTH = 6; + private final CharsFromStreamReader cut = new CharsFromStreamReader(MAX_LENGTH); + + @Test + void readPasswordShouldThrowExceptionWhenInputExceedsMaxLength() throws IOException { + try (BufferedReader input = new BufferedReader(new StringReader("very long password"))) { + assertThatThrownBy(() -> cut.readPassword(input)) + .isInstanceOf(IOException.class) + .hasMessageContaining(Integer.toString(MAX_LENGTH)); + } + } + + + @Test + void readPasswordShouldReturnThePassword() throws IOException { + // given + final String givenPass = "pass"; + BufferedReader input = new BufferedReader(new StringReader(givenPass)); + + // when + final CharSequence result = cut.readPassword(input); + + // then + assertThat(result.toString()).isEqualTo(givenPass); + } +} \ No newline at end of file diff --git a/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CryptPasswordTest.java b/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CryptPasswordTest.java new file mode 100644 index 00000000..debd9afb --- /dev/null +++ b/security/crypt-password/src/test/java/org/onap/dcaegen2/services/sdk/security/CryptPasswordTest.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START==================================== + * DCAEGEN2-SERVICES-SDK + * ========================================================= + * Copyright (C) 2019 Nokia. All rights reserved. + * ========================================================= + * Licensed 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. + * ============LICENSE_END===================================== + */ + +package org.onap.dcaegen2.services.sdk.security; + + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; + +class CryptPasswordTest { + + private final CryptPassword cut = new CryptPassword(); + + @Test + void encodedPasswordShouldMatchTheInput() { + final String rawPasswd = "some.strong.password"; + final String result = cut.encode(rawPasswd); + + assertThat(cut.matches(rawPasswd, result)).isTrue(); + } + + @Test + void testCompatibility() { + final String rawPasswd = "some.strong.password"; + final String encodedWithPreviousVersion = "$2a$10$LpP1jatprzTm9c4gX.jx7.k3.sa7Nm2aI7pe3hY/n6ZSo6g1Zye4K"; + + assertThat(cut.matches(rawPasswd, encodedWithPreviousVersion)).isTrue(); + } + + @Test + void differentPasswordShouldNotMatchTheInput() { + final String rawPasswd = "some.strong.password"; + final String result = cut.encode("different.password"); + + assertThat(cut.matches(rawPasswd, result)).isFalse(); + } +} \ No newline at end of file -- cgit 1.2.3-korg