Update dependencies and enhance crypt-password lib

Change-Id: I7fe56e43d7ff0ac9b50708a6abe2163f7ddf8904
Issue-ID: DCAEGEN2-1542
Signed-off-by: Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
diff --git a/security/crypt-password/pom.xml b/security/crypt-password/pom.xml
index 2e37c15..299aced 100644
--- a/security/crypt-password/pom.xml
+++ b/security/crypt-password/pom.xml
@@ -1,52 +1,70 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
-  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">
+        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">
 
-  <parent>
-    <groupId>org.onap.dcaegen2.services.sdk.security</groupId>
-    <artifactId>dcaegen2-services-sdk-security</artifactId>
-    <version>1.2.0-SNAPSHOT</version>
-  </parent>
-  <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.dcaegen2.services.sdk.security</groupId>
+        <artifactId>dcaegen2-services-sdk-security</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
 
-  <groupId>org.onap.dcaegen2.services.sdk.security.crypt</groupId>
-  <artifactId>crypt-password</artifactId>
+    <groupId>org.onap.dcaegen2.services.sdk.security.crypt</groupId>
+    <artifactId>crypt-password</artifactId>
 
-  <name>Security :: Crypt Password</name>
-  <description>DMaaP Security Module</description>
-  <packaging>jar</packaging>
+    <name>Security :: Crypt Password</name>
+    <description>DMaaP Security Module</description>
+    <packaging>jar</packaging>
 
-  <dependencies>
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-crypto</artifactId>
-      <version>3.1.0.RELEASE</version>
-    </dependency>
-  </dependencies>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-crypto</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+    </dependencies>
 
     <build>
         <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-shade-plugin</artifactId>
-            <version>3.2.1</version>
-            <executions>
-              <execution>
-                <phase>package</phase>
-                <goals>
-                  <goal>shade</goal>
-                </goals>
-                <configuration>
-                  <transformers>
-                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-                      <mainClass>org.onap.dcaegen2.services.sdk.security.DecodePassword</mainClass>
-                    </transformer>
-                  </transformers>
-                </configuration>
-              </execution>
-            </executions>
-          </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.onap.dcaegen2.services.sdk.security.EncodePassword</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
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 0000000..1ea18cc
--- /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 6ca78a0..4b16c9e 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 @@
 
 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 85412eb..0000000
--- 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 0000000..7784381
--- /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 0000000..51141ee
--- /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 0000000..2cfaa29
--- /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 0000000..debd9af
--- /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